diff --git a/pom.xml b/pom.xml
index f8516cf..d16ac36 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
xyz.zhouxy.plusone
plusone-commons
- 1.0.0-SNAPSHOT
+ 1.0.0-RC2
UTF-8
@@ -82,6 +82,20 @@
test
+
+ org.mybatis
+ mybatis
+ 3.5.17
+ test
+
+
+
+ com.h2database
+ h2
+ 2.2.224
+ test
+
+
com.fasterxml.jackson.core
diff --git a/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java b/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java
deleted file mode 100644
index 6e031c4..0000000
--- a/src/main/java/org/apache/ibatis/jdbc/AbstractSQL.java
+++ /dev/null
@@ -1,740 +0,0 @@
-/*
- * Copyright 2009-2023 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 org.apache.ibatis.jdbc;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author Clinton Begin
- * @author Jeff Butler
- * @author Adam Gent
- * @author Kazuki Shimizu
- */
-public abstract class AbstractSQL {
-
- private static final String AND = ") \nAND (";
- private static final String OR = ") \nOR (";
-
- private final SQLStatement sql = new SQLStatement();
-
- public abstract T getSelf();
-
- public T UPDATE(String table) {
- sql().statementType = SQLStatement.StatementType.UPDATE;
- sql().tables.add(table);
- return getSelf();
- }
-
- public T SET(String sets) {
- sql().sets.add(sets);
- return getSelf();
- }
-
- /**
- * Sets the.
- *
- * @param sets
- * the sets
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T SET(String... sets) {
- sql().sets.addAll(Arrays.asList(sets));
- return getSelf();
- }
-
- public T INSERT_INTO(String tableName) {
- sql().statementType = SQLStatement.StatementType.INSERT;
- sql().tables.add(tableName);
- return getSelf();
- }
-
- public T VALUES(String columns, String values) {
- INTO_COLUMNS(columns);
- INTO_VALUES(values);
- return getSelf();
- }
-
- /**
- * Into columns.
- *
- * @param columns
- * the columns
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T INTO_COLUMNS(String... columns) {
- sql().columns.addAll(Arrays.asList(columns));
- return getSelf();
- }
-
- /**
- * Into values.
- *
- * @param values
- * the values
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T INTO_VALUES(String... values) {
- List list = sql().valuesList.get(sql().valuesList.size() - 1);
- Collections.addAll(list, values);
- return getSelf();
- }
-
- public T SELECT(String columns) {
- sql().statementType = SQLStatement.StatementType.SELECT;
- sql().select.add(columns);
- return getSelf();
- }
-
- /**
- * Select.
- *
- * @param columns
- * the columns
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T SELECT(String... columns) {
- sql().statementType = SQLStatement.StatementType.SELECT;
- sql().select.addAll(Arrays.asList(columns));
- return getSelf();
- }
-
- public T SELECT_DISTINCT(String columns) {
- sql().distinct = true;
- SELECT(columns);
- return getSelf();
- }
-
- /**
- * Select distinct.
- *
- * @param columns
- * the columns
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T SELECT_DISTINCT(String... columns) {
- sql().distinct = true;
- SELECT(columns);
- return getSelf();
- }
-
- public T DELETE_FROM(String table) {
- sql().statementType = SQLStatement.StatementType.DELETE;
- sql().tables.add(table);
- return getSelf();
- }
-
- public T FROM(String table) {
- sql().tables.add(table);
- return getSelf();
- }
-
- /**
- * From.
- *
- * @param tables
- * the tables
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T FROM(String... tables) {
- sql().tables.addAll(Arrays.asList(tables));
- return getSelf();
- }
-
- public T JOIN(String join) {
- sql().join.add(join);
- return getSelf();
- }
-
- /**
- * Join.
- *
- * @param joins
- * the joins
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T JOIN(String... joins) {
- sql().join.addAll(Arrays.asList(joins));
- return getSelf();
- }
-
- public T INNER_JOIN(String join) {
- sql().innerJoin.add(join);
- return getSelf();
- }
-
- /**
- * Inner join.
- *
- * @param joins
- * the joins
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T INNER_JOIN(String... joins) {
- sql().innerJoin.addAll(Arrays.asList(joins));
- return getSelf();
- }
-
- public T LEFT_OUTER_JOIN(String join) {
- sql().leftOuterJoin.add(join);
- return getSelf();
- }
-
- /**
- * Left outer join.
- *
- * @param joins
- * the joins
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T LEFT_OUTER_JOIN(String... joins) {
- sql().leftOuterJoin.addAll(Arrays.asList(joins));
- return getSelf();
- }
-
- public T RIGHT_OUTER_JOIN(String join) {
- sql().rightOuterJoin.add(join);
- return getSelf();
- }
-
- /**
- * Right outer join.
- *
- * @param joins
- * the joins
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T RIGHT_OUTER_JOIN(String... joins) {
- sql().rightOuterJoin.addAll(Arrays.asList(joins));
- return getSelf();
- }
-
- public T OUTER_JOIN(String join) {
- sql().outerJoin.add(join);
- return getSelf();
- }
-
- /**
- * Outer join.
- *
- * @param joins
- * the joins
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T OUTER_JOIN(String... joins) {
- sql().outerJoin.addAll(Arrays.asList(joins));
- return getSelf();
- }
-
- public T WHERE(String conditions) {
- sql().where.add(conditions);
- sql().lastList = sql().where;
- return getSelf();
- }
-
- /**
- * Where.
- *
- * @param conditions
- * the conditions
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T WHERE(String... conditions) {
- sql().where.addAll(Arrays.asList(conditions));
- sql().lastList = sql().where;
- return getSelf();
- }
-
- public T OR() {
- sql().lastList.add(OR);
- return getSelf();
- }
-
- public T AND() {
- sql().lastList.add(AND);
- return getSelf();
- }
-
- public T GROUP_BY(String columns) {
- sql().groupBy.add(columns);
- return getSelf();
- }
-
- /**
- * Group by.
- *
- * @param columns
- * the columns
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T GROUP_BY(String... columns) {
- sql().groupBy.addAll(Arrays.asList(columns));
- return getSelf();
- }
-
- public T HAVING(String conditions) {
- sql().having.add(conditions);
- sql().lastList = sql().having;
- return getSelf();
- }
-
- /**
- * Having.
- *
- * @param conditions
- * the conditions
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T HAVING(String... conditions) {
- sql().having.addAll(Arrays.asList(conditions));
- sql().lastList = sql().having;
- return getSelf();
- }
-
- public T ORDER_BY(String columns) {
- sql().orderBy.add(columns);
- return getSelf();
- }
-
- /**
- * Order by.
- *
- * @param columns
- * the columns
- *
- * @return the t
- *
- * @since 3.4.2
- */
- public T ORDER_BY(String... columns) {
- sql().orderBy.addAll(Arrays.asList(columns));
- return getSelf();
- }
-
- /**
- * Set the limit variable string(e.g. {@code "#{limit}"}).
- *
- * @param variable
- * a limit variable string
- *
- * @return a self instance
- *
- * @see #OFFSET(String)
- *
- * @since 3.5.2
- */
- public T LIMIT(String variable) {
- sql().limit = variable;
- sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
- return getSelf();
- }
-
- /**
- * Set the limit value.
- *
- * @param value
- * an offset value
- *
- * @return a self instance
- *
- * @see #OFFSET(long)
- *
- * @since 3.5.2
- */
- public T LIMIT(int value) {
- return LIMIT(String.valueOf(value));
- }
-
- /**
- * Set the offset variable string(e.g. {@code "#{offset}"}).
- *
- * @param variable
- * a offset variable string
- *
- * @return a self instance
- *
- * @see #LIMIT(String)
- *
- * @since 3.5.2
- */
- public T OFFSET(String variable) {
- sql().offset = variable;
- sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
- return getSelf();
- }
-
- /**
- * Set the offset value.
- *
- * @param value
- * an offset value
- *
- * @return a self instance
- *
- * @see #LIMIT(int)
- *
- * @since 3.5.2
- */
- public T OFFSET(long value) {
- return OFFSET(String.valueOf(value));
- }
-
- /**
- * Set the fetch first rows variable string(e.g. {@code "#{fetchFirstRows}"}).
- *
- * @param variable
- * a fetch first rows variable string
- *
- * @return a self instance
- *
- * @see #OFFSET_ROWS(String)
- *
- * @since 3.5.2
- */
- public T FETCH_FIRST_ROWS_ONLY(String variable) {
- sql().limit = variable;
- sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
- return getSelf();
- }
-
- /**
- * Set the fetch first rows value.
- *
- * @param value
- * a fetch first rows value
- *
- * @return a self instance
- *
- * @see #OFFSET_ROWS(long)
- *
- * @since 3.5.2
- */
- public T FETCH_FIRST_ROWS_ONLY(int value) {
- return FETCH_FIRST_ROWS_ONLY(String.valueOf(value));
- }
-
- /**
- * Set the offset rows variable string(e.g. {@code "#{offset}"}).
- *
- * @param variable
- * a offset rows variable string
- *
- * @return a self instance
- *
- * @see #FETCH_FIRST_ROWS_ONLY(String)
- *
- * @since 3.5.2
- */
- public T OFFSET_ROWS(String variable) {
- sql().offset = variable;
- sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
- return getSelf();
- }
-
- /**
- * Set the offset rows value.
- *
- * @param value
- * an offset rows value
- *
- * @return a self instance
- *
- * @see #FETCH_FIRST_ROWS_ONLY(int)
- *
- * @since 3.5.2
- */
- public T OFFSET_ROWS(long value) {
- return OFFSET_ROWS(String.valueOf(value));
- }
-
- /**
- * used to add a new inserted row while do multi-row insert.
- *
- * @return the t
- *
- * @since 3.5.2
- */
- public T ADD_ROW() {
- sql().valuesList.add(new ArrayList<>());
- return getSelf();
- }
-
- private SQLStatement sql() {
- return sql;
- }
-
- public A usingAppender(A a) {
- sql().sql(a);
- return a;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sql().sql(sb);
- return sb.toString();
- }
-
- private static class SafeAppendable {
- private final Appendable appendable;
- private boolean empty = true;
-
- public SafeAppendable(Appendable a) {
- this.appendable = a;
- }
-
- public SafeAppendable append(CharSequence s) {
- try {
- if (empty && s.length() > 0) {
- empty = false;
- }
- appendable.append(s);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return this;
- }
-
- public boolean isEmpty() {
- return empty;
- }
-
- }
-
- private static class SQLStatement {
-
- public enum StatementType {
-
- DELETE,
-
- INSERT,
-
- SELECT,
-
- UPDATE
-
- }
-
- private enum LimitingRowsStrategy {
- NOP {
- @Override
- protected void appendClause(SafeAppendable builder, String offset, String limit) {
- // NOP
- }
- },
- ISO {
- @Override
- protected void appendClause(SafeAppendable builder, String offset, String limit) {
- if (offset != null) {
- builder.append(" OFFSET ").append(offset).append(" ROWS");
- }
- if (limit != null) {
- builder.append(" FETCH FIRST ").append(limit).append(" ROWS ONLY");
- }
- }
- },
- OFFSET_LIMIT {
- @Override
- protected void appendClause(SafeAppendable builder, String offset, String limit) {
- if (limit != null) {
- builder.append(" LIMIT ").append(limit);
- }
- if (offset != null) {
- builder.append(" OFFSET ").append(offset);
- }
- }
- };
-
- protected abstract void appendClause(SafeAppendable builder, String offset, String limit);
-
- }
-
- StatementType statementType;
- List sets = new ArrayList<>();
- List select = new ArrayList<>();
- List tables = new ArrayList<>();
- List join = new ArrayList<>();
- List innerJoin = new ArrayList<>();
- List outerJoin = new ArrayList<>();
- List leftOuterJoin = new ArrayList<>();
- List rightOuterJoin = new ArrayList<>();
- List where = new ArrayList<>();
- List having = new ArrayList<>();
- List groupBy = new ArrayList<>();
- List orderBy = new ArrayList<>();
- List lastList = new ArrayList<>();
- List columns = new ArrayList<>();
- List> valuesList = new ArrayList<>();
- boolean distinct;
- String offset;
- String limit;
- LimitingRowsStrategy limitingRowsStrategy = LimitingRowsStrategy.NOP;
-
- public SQLStatement() {
- // Prevent Synthetic Access
- valuesList.add(new ArrayList<>());
- }
-
- private void sqlClause(SafeAppendable builder, String keyword, List parts, String open, String close,
- String conjunction) {
- if (!parts.isEmpty()) {
- if (!builder.isEmpty()) {
- builder.append("\n");
- }
- builder.append(keyword);
- builder.append(" ");
- builder.append(open);
- String last = "________";
- for (int i = 0, n = parts.size(); i < n; i++) {
- String part = parts.get(i);
- if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
- builder.append(conjunction);
- }
- builder.append(part);
- last = part;
- }
- builder.append(close);
- }
- }
-
- private String selectSQL(SafeAppendable builder) {
- if (distinct) {
- sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
- } else {
- sqlClause(builder, "SELECT", select, "", "", ", ");
- }
-
- sqlClause(builder, "FROM", tables, "", "", ", ");
- joins(builder);
- sqlClause(builder, "WHERE", where, "(", ")", " AND ");
- sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
- sqlClause(builder, "HAVING", having, "(", ")", " AND ");
- sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
- limitingRowsStrategy.appendClause(builder, offset, limit);
- return builder.toString();
- }
-
- private void joins(SafeAppendable builder) {
- sqlClause(builder, "JOIN", join, "", "", "\nJOIN ");
- sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN ");
- sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN ");
- sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN ");
- sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN ");
- }
-
- private String insertSQL(SafeAppendable builder) {
- sqlClause(builder, "INSERT INTO", tables, "", "", "");
- sqlClause(builder, "", columns, "(", ")", ", ");
- for (int i = 0; i < valuesList.size(); i++) {
- sqlClause(builder, i > 0 ? "," : "VALUES", valuesList.get(i), "(", ")", ", ");
- }
- return builder.toString();
- }
-
- private String deleteSQL(SafeAppendable builder) {
- sqlClause(builder, "DELETE FROM", tables, "", "", "");
- sqlClause(builder, "WHERE", where, "(", ")", " AND ");
- limitingRowsStrategy.appendClause(builder, null, limit);
- return builder.toString();
- }
-
- private String updateSQL(SafeAppendable builder) {
- sqlClause(builder, "UPDATE", tables, "", "", "");
- joins(builder);
- sqlClause(builder, "SET", sets, "", "", ", ");
- sqlClause(builder, "WHERE", where, "(", ")", " AND ");
- limitingRowsStrategy.appendClause(builder, null, limit);
- return builder.toString();
- }
-
- public String sql(Appendable a) {
- SafeAppendable builder = new SafeAppendable(a);
- if (statementType == null) {
- return null;
- }
-
- String answer;
-
- switch (statementType) {
- case DELETE:
- answer = deleteSQL(builder);
- break;
-
- case INSERT:
- answer = insertSQL(builder);
- break;
-
- case SELECT:
- answer = selectSQL(builder);
- break;
-
- case UPDATE:
- answer = updateSQL(builder);
- break;
-
- default:
- answer = null;
- }
-
- return answer;
- }
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/BoolRef.java b/src/main/java/xyz/zhouxy/plusone/commons/base/BoolRef.java
deleted file mode 100644
index 56918ad..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/BoolRef.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2024 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.base;
-
-import com.google.common.annotations.Beta;
-
-import xyz.zhouxy.plusone.commons.function.BoolUnaryOperator;
-
-@Beta
-public class BoolRef {
-
- private boolean value;
-
- public BoolRef(boolean value) {
- this.value = value;
- }
-
- public boolean getValue() {
- return value;
- }
-
- public void setValue(boolean value) {
- this.value = value;
- }
-
- public void apply(BoolUnaryOperator operator) {
- this.value = operator.applyAsBool(this.value);
- }
-
- @Override
- public String toString() {
- return String.format("BoolRef[%s]", value);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + (value ? 1231 : 1237);
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- return value == ((BoolRef) obj).value;
- }
-
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/CharRef.java b/src/main/java/xyz/zhouxy/plusone/commons/base/CharRef.java
deleted file mode 100644
index 929d4b4..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/CharRef.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2024 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.base;
-
-import com.google.common.annotations.Beta;
-
-import xyz.zhouxy.plusone.commons.function.CharUnaryOperator;
-
-@Beta
-public class CharRef {
-
- private char value;
-
- public CharRef(char value) {
- this.value = value;
- }
-
- public char getValue() {
- return value;
- }
-
- public void setValue(char value) {
- this.value = value;
- }
-
- public void apply(CharUnaryOperator operator) {
- this.value = operator.applyAsChar(this.value);
- }
-
- @Override
- public String toString() {
- return String.format("CharRef[%s]", value);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + value;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- return value == ((CharRef) obj).value;
- }
-
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/DoubleRef.java b/src/main/java/xyz/zhouxy/plusone/commons/base/DoubleRef.java
deleted file mode 100644
index 9c476e5..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/DoubleRef.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2024 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.base;
-
-import java.util.function.DoubleUnaryOperator;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public class DoubleRef {
-
- private double value;
-
- public DoubleRef(double value) {
- this.value = value;
- }
-
- public double getValue() {
- return value;
- }
-
- public void setValue(double value) {
- this.value = value;
- }
-
- public void apply(DoubleUnaryOperator operator) {
- this.value = operator.applyAsDouble(this.value);
- }
-
- @Override
- public String toString() {
- return String.format("DoubleRef[%s]", value);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- long temp;
- temp = Double.doubleToLongBits(value);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- final DoubleRef other = (DoubleRef) obj;
- return Double.doubleToLongBits(value) == Double.doubleToLongBits(other.value);
- }
-
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/IWithCode.java b/src/main/java/xyz/zhouxy/plusone/commons/base/IWithCode.java
index 015fdc3..5633567 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/IWithCode.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/base/IWithCode.java
@@ -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.
@@ -19,6 +19,7 @@ package xyz.zhouxy.plusone.commons.base;
import java.util.Objects;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* 规定实现类带有 {@code getCode} 方法。
@@ -31,7 +32,19 @@ public interface IWithCode {
@Nonnull
T getCode();
- default boolean equalsCode(T code) {
+ default boolean isCodeEquals(@Nullable T code) {
return Objects.equals(getCode(), code);
}
+
+ default boolean isSameCodeAs(@Nullable IWithCode> other) {
+ return other != null && Objects.equals(getCode(), other.getCode());
+ }
+
+ default boolean isSameCodeAs(@Nullable IWithIntCode other) {
+ return other != null && Objects.equals(getCode(), other.getCode());
+ }
+
+ default boolean isSameCodeAs(@Nullable IWithLongCode other) {
+ return other != null && Objects.equals(getCode(), other.getCode());
+ }
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/IWithIntCode.java b/src/main/java/xyz/zhouxy/plusone/commons/base/IWithIntCode.java
index 7deac35..3e881cc 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/IWithIntCode.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/base/IWithIntCode.java
@@ -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.
@@ -16,6 +16,10 @@
package xyz.zhouxy.plusone.commons.base;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+
/**
* 规定实现类带有 {@code getCode} 方法。
* 用于像自定义异常等需要带有 {@code code} 字段的类,
@@ -26,7 +30,19 @@ package xyz.zhouxy.plusone.commons.base;
public interface IWithIntCode {
int getCode();
- default boolean equalsCode(int code) {
+ default boolean isCodeEquals(int code) {
return getCode() == code;
}
+
+ default boolean isSameCodeAs(@Nullable IWithCode> other) {
+ return other != null && Objects.equals(getCode(), other.getCode());
+ }
+
+ default boolean isSameCodeAs(@Nullable IWithIntCode other) {
+ return other != null && getCode() == other.getCode();
+ }
+
+ default boolean isSameCodeAs(@Nullable IWithLongCode other) {
+ return other != null && getCode() == other.getCode();
+ }
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/IWithLongCode.java b/src/main/java/xyz/zhouxy/plusone/commons/base/IWithLongCode.java
index 1bd8d20..b10d8a2 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/IWithLongCode.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/base/IWithLongCode.java
@@ -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.
@@ -16,6 +16,10 @@
package xyz.zhouxy.plusone.commons.base;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+
/**
* 规定实现类带有 {@code getCode} 方法。
* 用于像自定义异常等需要带有 {@code code} 字段的类,
@@ -26,7 +30,19 @@ package xyz.zhouxy.plusone.commons.base;
public interface IWithLongCode {
long getCode();
- default boolean equalsCode(long code) {
+ default boolean isCodeEquals(long code) {
return getCode() == code;
}
+
+ default boolean isSameCodeAs(@Nullable IWithCode> other) {
+ return other != null && Objects.equals(getCode(), other.getCode());
+ }
+
+ default boolean isSameCodeAs(@Nullable IWithIntCode other) {
+ return other != null && getCode() == other.getCode();
+ }
+
+ default boolean isSameCodeAs(@Nullable IWithLongCode other) {
+ return other != null && getCode() == other.getCode();
+ }
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/IntRef.java b/src/main/java/xyz/zhouxy/plusone/commons/base/IntRef.java
deleted file mode 100644
index 8f0148b..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/IntRef.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2024 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.base;
-
-import java.util.function.IntUnaryOperator;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public class IntRef {
-
- private int value;
-
- public IntRef(int value) {
- this.value = value;
- }
-
- public int getValue() {
- return value;
- }
-
- public void setValue(int value) {
- this.value = value;
- }
-
- public void apply(IntUnaryOperator operator) {
- this.value = operator.applyAsInt(this.value);
- }
-
- @Override
- public String toString() {
- return String.format("IntRef[%s]", value);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + value;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- return value == ((IntRef) obj).value;
- }
-
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java b/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java
deleted file mode 100644
index 957d309..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/JRE.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2023-2024 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.base;
-
-import xyz.zhouxy.plusone.commons.util.StringTools;
-
-/**
- * JRE version
- */
-public class JRE {
-
- private static final int JAVA_8 = 8;
-
- public static final int CURRENT_VERSION = getJre();
-
- public static boolean isJava8() {
- return CURRENT_VERSION == JAVA_8;
- }
-
- private static int getJre() {
- String version = System.getProperty("java.version");
- boolean isNotBlank = StringTools.isNotBlank(version);
- if (isNotBlank && version.startsWith("1.8")) {
- return JAVA_8;
- }
- // if JRE version is 9 or above, we can get version from
- // java.lang.Runtime.version()
- try {
- return getMajorVersion(version);
- } catch (Exception e) {
- // assuming that JRE version is 8.
- }
- // default java 8
- return JAVA_8;
- }
-
- private static int getMajorVersion(String version) {
- if (version.startsWith("1.")) {
- return Integer.parseInt(version.substring(2, 3));
- } else {
- int dotIndex = version.indexOf(".");
- return (dotIndex != -1) ? Integer.parseInt(version.substring(0, dotIndex)) : Integer.parseInt(version);
- }
- }
-
- private JRE() {
- throw new IllegalStateException("Utility class");
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/LongRef.java b/src/main/java/xyz/zhouxy/plusone/commons/base/LongRef.java
deleted file mode 100644
index 7d079bd..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/LongRef.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2024 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.base;
-
-import java.util.function.LongUnaryOperator;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public class LongRef {
-
- private long value;
-
- public LongRef(long value) {
- this.value = value;
- }
-
- public long getValue() {
- return value;
- }
-
- public void setValue(long value) {
- this.value = value;
- }
-
- public void apply(LongUnaryOperator operator) {
- this.value = operator.applyAsLong(this.value);
- }
-
- @Override
- public String toString() {
- return String.format("LongRef[%s]", value);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + (int) (value ^ (value >>> 32));
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- return value == ((LongRef) obj).value;
- }
-
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/Ref.java b/src/main/java/xyz/zhouxy/plusone/commons/base/Ref.java
index 94c5ee4..e5243b4 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/base/Ref.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/base/Ref.java
@@ -18,35 +18,100 @@ package xyz.zhouxy.plusone.commons.base;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.function.UnaryOperator;
-import com.google.common.annotations.Beta;
+import javax.annotation.Nullable;
-@Beta
+/**
+ * {@link Ref} 包装了一个值,表示对该值的应用。
+ *
+ * 灵感来自于 C# 的 {@value ref} 参数修饰符。C# 允许通过以下方式,将值返回给调用端:
+ *
+ * void Method(ref int refArgument)
+ * {
+ * refArgument = refArgument + 44;
+ * }
+ *
+ * int number = 1;
+ * Method(ref number);
+ * Console.WriteLine(number); // Output: 45
+ *
+ * {@link Ref} 使 Java 可以达到类似的效果,如:
+ *
+ * void method(final Ref<Integer> refArgument) {
+ * refArgument.transformValue(i -> i + 44);
+ * }
+ *
+ * Ref<Integer> number = Ref.of(1);
+ * method(number);
+ * System.out.println(number.getValue()); // Output: 45
+ *
+ *
+ * 当一个方法需要产生多个结果时,无法有多个返回值,可以使用 {@link Ref} 作为参数传入,方法内部修改 {@link Ref} 的值。
+ * 调用方在调用方法之后,使用 {@code getValue()} 获取结果。
+ *
+ *
+ * String method(final Ref<Integer> intRefArgument, final 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
+ *
+ *
+ * @author ZhouXY
+ * @since 1.0.0
+ */
public final class Ref {
+ @Nullable
private T value;
- public Ref() {
- this.value = null;
- }
-
- public Ref(T value) {
+ private Ref(@Nullable T value) {
this.value = value;
}
+ public static Ref of(@Nullable T value) {
+ return new Ref<>(value);
+ }
+
+ public static Ref empty() {
+ return new Ref<>(null);
+ }
+
+ @Nullable
public T getValue() {
return value;
}
- public void setValue(T value) {
+ public void setValue(@Nullable T value) {
this.value = value;
}
- public void transform(UnaryOperator operator) {
+ public void transformValue(UnaryOperator operator) {
this.value = operator.apply(this.value);
}
+ public Ref transform(Function super T, R> function) {
+ return Ref.of(function.apply(this.value));
+ }
+
+ public boolean checkValue(Predicate super T> predicate) {
+ return predicate.test(this.value);
+ }
+
+ public void execute(Consumer super T> consumer) {
+ consumer.accept(value);
+ }
+
public boolean isNull() {
return this.value == null;
}
@@ -55,10 +120,6 @@ public final class Ref {
return this.value != null;
}
- public void execute(Consumer super T> consumer) {
- consumer.accept(value);
- }
-
@Override
public String toString() {
return String.format("Ref[%s]", value);
@@ -73,7 +134,7 @@ public final class Ref {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if (obj == null)
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/base/package-info.java b/src/main/java/xyz/zhouxy/plusone/commons/base/package-info.java
new file mode 100644
index 0000000..523b74d
--- /dev/null
+++ b/src/main/java/xyz/zhouxy/plusone/commons/base/package-info.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+/**
+ * 基础组件
+ *
+ * Ref
+ *
+ * {@link Ref} 包装了一个值,表示对该值的应用。
+ *
+ * 灵感来自于 C# 的 {@value ref} 参数修饰符。C# 允许通过以下方式,将值返回给调用端:
+ *
+ * void Method(ref int refArgument)
+ * {
+ * refArgument = refArgument + 44;
+ * }
+ *
+ * int number = 1;
+ * Method(ref number);
+ * Console.WriteLine(number); // Output: 45
+ *
+ * {@link Ref} 使 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
+ *
+ *
+ * 当一个方法需要产生多个结果时,无法有多个返回值,可以使用 {@link Ref} 作为参数传入,方法内部修改 {@link Ref} 的值。
+ * 调用方在调用方法之后,使用 {@code getValue()} 获取结果。
+ *
+ *
+ * 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
+ *
+ *
+ * IWithCode
+ *
+ * 类似于枚举之类的类,通常需要设置固定的码值表示对应的含义。
+ * 可实现 {@link IWithCode}、{@link IWithIntCode}、{@link IWithLongCode},便于在需要的地方对这些接口的实现进行处理。
+ *
+ */
+@CheckReturnValue
+@ParametersAreNonnullByDefault
+package xyz.zhouxy.plusone.commons.base;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+import javax.annotation.CheckReturnValue;
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java
deleted file mode 100644
index f8a9d21..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/collection/AbstractMapWrapper.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright 2023-2024 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.collection;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import com.google.common.annotations.Beta;
-
-import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools;
-
-@Beta
-public abstract class AbstractMapWrapper> {
-
- private final Map map;
- private final Consumer keyChecker;
- private final Consumer valueChecker;
-
- protected AbstractMapWrapper(Map map, @Nullable Consumer keyChecker, @Nullable Consumer valueChecker) {
- this.map = map;
- this.keyChecker = keyChecker;
- this.valueChecker = valueChecker;
- }
-
- public final T put(K key, V value) {
- if (this.keyChecker != null) {
- this.keyChecker.accept(key);
- }
- if (this.valueChecker != null) {
- this.valueChecker.accept(value);
- }
- this.map.put(key, value);
- return getSelf();
- }
-
- public final T putAll(Map extends K, ? extends V> m) {
- m.forEach(this::put);
- return getSelf();
- }
-
- /**
- * 获取 {@code map} 中的值。如果 {@code key} 不存在,则抛出异常。
- * 将 {@code value}(可为 {@code null})装进 {@link Optional} 中后返回。
- * 为了这碟醋包的这盘饺子。
- *
- * @param key 键
- * @return 可缺失的值
- * @throws IllegalArgumentException key 不存在时抛出。
- */
- public Optional get(K key) {
- if (!this.map.containsKey(key)) {
- throw new IllegalArgumentException("Key does not exist");
- }
- return Optional.ofNullable(this.map.get(key));
- }
-
- @SuppressWarnings("unchecked")
- public final Optional getAndConvert(K key) {
- return get(key).map(v -> (R) v);
- }
-
- public final Optional getAndConvert(K key, Function mapper) {
- return get(key).map(mapper);
- }
-
- public final boolean containsKey(Object key) {
- return this.map.containsKey(key);
- }
-
- public final int size() {
- return this.map.size();
- }
-
- public final Set keySet() {
- return this.map.keySet();
- }
-
- public final Collection values() {
- return this.map.values();
- }
-
- public final Set> entrySet() {
- return this.map.entrySet();
- }
-
- public final void clear() {
- this.map.clear();
- }
-
- public final boolean containsValue(Object value) {
- return this.map.containsValue(value);
- }
-
- public final boolean isEmpty() {
- return this.map.isEmpty();
- }
-
- public final V remove(Object key) {
- return this.map.remove(key);
- }
-
- public final V putIfAbsent(K key, V value) {
- if (this.keyChecker != null) {
- this.keyChecker.accept(key);
- }
- if (this.valueChecker != null) {
- this.valueChecker.accept(value);
- }
- return this.map.putIfAbsent(key, value);
- }
-
- public final V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
- if (this.keyChecker != null) {
- this.keyChecker.accept(key);
- }
- Function super K, ? extends V> func = (K k) -> {
- V value = mappingFunction.apply(k);
- if (this.valueChecker != null) {
- this.valueChecker.accept(value);
- }
- return value;
- };
- if (this.map instanceof ConcurrentHashMap) {
- return ConcurrentHashMapTools.computeIfAbsent(
- (ConcurrentHashMap) this.map, key, func);
- } else {
- return this.map.computeIfAbsent(key, func);
- }
- }
-
- public final Map exportMap() {
- return this.map;
- }
-
- public final Map exportUnmodifiableMap() {
- return Collections.unmodifiableMap(this.map);
- }
-
- protected abstract T getSelf();
-
- @Override
- public String toString() {
- return this.map.toString();
- }
-
- public abstract static class Builder> {
- protected final Map map;
- protected Consumer keyChecker;
- protected Consumer valueChecker;
-
- protected Builder(Map map) {
- this.map = map;
- }
-
- public Builder keyChecker(@Nullable Consumer keyChecker) {
- this.keyChecker = keyChecker;
- return this;
- }
-
- public Builder valueChecker(@Nullable Consumer valueChecker) {
- this.valueChecker = valueChecker;
- return this;
- }
-
- public Builder put(K key, V value) {
- if (this.keyChecker != null) {
- this.keyChecker.accept(key);
- }
- if (this.valueChecker != null) {
- this.valueChecker.accept(value);
- }
- this.map.put(key, value);
- return this;
- }
-
- public Builder putAll(Map extends K, ? extends V> m) {
- m.forEach(this::put);
- return this;
- }
-
- public abstract T build();
-
- public abstract T buildUnmodifiableMap();
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/CollectionTools.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/CollectionTools.java
index 446ab37..f5e671b 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/collection/CollectionTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/collection/CollectionTools.java
@@ -17,12 +17,24 @@
package xyz.zhouxy.plusone.commons.collection;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+/**
+ * 集合工具类
+ *
+ * @author ZhouXY
+ * @since 0.1.0
+ */
public class CollectionTools {
+ // TODO [添加] 新增其它集合类型,如 guava 的扩展集合等
+
// isEmpty
public static boolean isEmpty(@Nullable Collection> collection) {
@@ -43,6 +55,21 @@ public class CollectionTools {
return map != null && !map.isEmpty();
}
+ @Nonnull
+ public static List nullToEmptyList(@Nullable List list) {
+ return list == null ? Collections.emptyList() : list;
+ }
+
+ @Nonnull
+ public static Set nullToEmptySet(@Nullable Set set) {
+ return set == null ? Collections.emptySet() : set;
+ }
+
+ @Nonnull
+ public static Map nullToEmptyMap(@Nullable Map map) {
+ return map == null ? Collections.emptyMap() : map;
+ }
+
private CollectionTools() {
throw new IllegalStateException("Utility class");
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/MapWrapper.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/MapWrapper.java
deleted file mode 100644
index c928ae6..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/collection/MapWrapper.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2023-2024 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.collection;
-
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.function.Consumer;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public final class MapWrapper extends AbstractMapWrapper> {
-
- private MapWrapper(Map map, Consumer keyChecker, Consumer valueChecker) {
- super(map, keyChecker, valueChecker);
- }
-
- public static Builder wrap(Map map) {
- return new Builder<>(map);
- }
-
- public static Builder wrapHashMap() {
- return new Builder<>(new HashMap<>());
- }
-
- public static Builder wrapHashMap(int initialCapacity) {
- return new Builder<>(new HashMap<>(initialCapacity));
- }
-
- public static Builder wrapHashMap(int initialCapacity, float loadFactor) {
- return new Builder<>(new HashMap<>(initialCapacity, loadFactor));
- }
-
- public static , V> Builder wrapTreeMap() {
- return new Builder<>(new TreeMap<>());
- }
-
- public static Builder wrapTreeMap(SortedMap m) {
- return new Builder<>(new TreeMap<>(m));
- }
-
- public static Builder wrapTreeMap(Comparator super K> comparator) {
- return new Builder<>(new TreeMap<>(comparator));
- }
-
- public static final class Builder extends AbstractMapWrapper.Builder> {
-
- private Builder(Map map) {
- super(map);
- }
-
- @Override
- public MapWrapper build() {
- return new MapWrapper<>(map, keyChecker, valueChecker);
- }
-
- @Override
- public MapWrapper buildUnmodifiableMap() {
- return new MapWrapper<>(Collections.unmodifiableMap(map), keyChecker, valueChecker);
- }
- }
-
- @Override
- protected MapWrapper getSelf() {
- return this;
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/ReadWriteLockedTable.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/ReadWriteLockedTable.java
deleted file mode 100644
index a89138e..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/collection/ReadWriteLockedTable.java
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright 2023-2024 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.collection;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableTable;
-import com.google.common.collect.Table;
-import xyz.zhouxy.plusone.commons.annotation.ReaderMethod;
-import xyz.zhouxy.plusone.commons.annotation.WriterMethod;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.ThreadSafe;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * 使用 {@link ReentrantReadWriteLock} 将 {@link Table} 包装为线程安全的集合。
- *
- *
- * 可通过以下方式构建一个线程安全的 {@link Table}
- *
- *
- *
- * LockedTable.of(HashBasedTable.create())
- *
- *
- *
- * NOTE: 如果 {@link Table} 不需要更改,请使用 {@link ImmutableTable}
- *
- *
- * @author ZhouXY
- * @see Table
- * @see ImmutableTable
- * @see ReentrantReadWriteLock
- * @since 0.1.0-SNAPSHOT
- */
-@Beta
-@ThreadSafe
-public class ReadWriteLockedTable implements Table {
-
- private final Table table;
-
- private final ReentrantReadWriteLock.ReadLock readLock;
- private final ReentrantReadWriteLock.WriteLock writeLock;
-
- private ReadWriteLockedTable(Table table, boolean fair) {
- this.table = Objects.requireNonNull(table);
- ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(fair);
- this.readLock = rwl.readLock();
- this.writeLock = rwl.writeLock();
- }
-
- public static ReadWriteLockedTable of(Table table) {
- if (table instanceof ReadWriteLockedTable) {
- return (ReadWriteLockedTable) table;
- } else {
- return new ReadWriteLockedTable<>(table, false);
- }
- }
-
- public static ReadWriteLockedTable of(Table table, boolean fair) {
- if (table instanceof ReadWriteLockedTable) {
- return (ReadWriteLockedTable) table;
- } else {
- return new ReadWriteLockedTable<>(table, fair);
- }
- }
-
- @Override
- @ReaderMethod
- public boolean contains(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
- readLock.lock();
- try {
- return this.table.contains(rowKey, columnKey);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public boolean containsRow(@CheckForNull @Nonnull Object rowKey) {
- readLock.lock();
- try {
- return this.table.containsRow(rowKey);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public boolean containsColumn(@CheckForNull @Nonnull Object columnKey) {
- readLock.lock();
- try {
- return this.table.containsColumn(columnKey);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public boolean containsValue(@CheckForNull @Nonnull Object value) {
- readLock.lock();
- try {
- return this.table.containsValue(value);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public V get(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
- readLock.lock();
- try {
- return this.table.get(rowKey, columnKey);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public boolean isEmpty() {
- readLock.lock();
- try {
- return this.table.isEmpty();
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public int size() {
- readLock.lock();
- try {
- return this.table.size();
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @WriterMethod
- public void clear() {
- writeLock.lock();
- try {
- this.table.clear();
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- @WriterMethod
- public V put(@CheckForNull @Nonnull R rowKey,
- @CheckForNull @Nonnull C columnKey,
- @CheckForNull @Nonnull V value) {
- writeLock.lock();
- try {
- return this.table.put(rowKey, columnKey, value);
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- @WriterMethod
- public void putAll(@Nonnull Table extends R, ? extends C, ? extends V> table) {
- writeLock.lock();
- try {
- this.table.putAll(table);
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- @WriterMethod
- public V remove(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
- writeLock.lock();
- try {
- return this.table.remove(rowKey, columnKey);
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Map row(@CheckForNull @Nonnull R rowKey) {
- readLock.lock();
- try {
- return this.table.row(rowKey);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Map column(@CheckForNull @Nonnull C columnKey) {
- readLock.lock();
- try {
- return this.table.column(columnKey);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Set> cellSet() {
- readLock.lock();
- try {
- return this.table.cellSet();
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Set rowKeySet() {
- readLock.lock();
- try {
- return this.table.rowKeySet();
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Set columnKeySet() {
- readLock.lock();
- try {
- return this.table.columnKeySet();
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Collection values() {
- readLock.lock();
- try {
- return this.table.values();
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Map> rowMap() {
- readLock.lock();
- try {
- return this.table.rowMap();
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- @ReaderMethod
- public Map> columnMap() {
- readLock.lock();
- try {
- return this.table.columnMap();
- } finally {
- readLock.unlock();
- }
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java b/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java
deleted file mode 100644
index 0b60882..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/collection/SafeConcurrentHashMap.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2023-2024 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.collection;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-import xyz.zhouxy.plusone.commons.base.JRE;
-import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools;
-
-/**
- * SafeConcurrentHashMap
- *
- *
- * Java 8 的 {@link ConcurrentHashMap#computeIfAbsent(Object, Function)} 方法有 bug,
- * 使用 Java 8 时,可使用这个类进行替换。
- *
- * @author ZhouXY
- * @since 1.0
- * @see ConcurrentHashMap
- * @see ConcurrentHashMapTools#computeIfAbsentForJava8(ConcurrentHashMap, Object, Function)
- */
-@ThreadSafe
-public class SafeConcurrentHashMap extends ConcurrentHashMap {
-
- private static final long serialVersionUID = 4352954948768449595L;
-
- /**
- * Creates a new, empty map with the default initial table size (16).
- */
- public SafeConcurrentHashMap() {
- }
-
- /**
- * Creates a new, empty map with an initial table size
- * accommodating the specified number of elements without the need
- * to dynamically resize.
- *
- * @param initialCapacity The implementation performs internal
- * sizing to accommodate this many elements.
- * @throws IllegalArgumentException if the initial capacity of
- * elements is negative
- */
- public SafeConcurrentHashMap(int initialCapacity) {
- super(initialCapacity);
- }
-
- /**
- * Creates a new map with the same mappings as the given map.
- *
- * @param m the map
- */
- public SafeConcurrentHashMap(Map extends K, ? extends V> m) {
- super(m);
- }
-
- /**
- * Creates a new, empty map with an initial table size based on
- * the given number of elements ({@code initialCapacity}) and
- * initial table density ({@code loadFactor}).
- *
- * @param initialCapacity the initial capacity. The implementation
- * performs internal sizing to accommodate this many elements,
- * given the specified load factor.
- * @param loadFactor the load factor (table density) for
- * establishing the initial table size
- * @throws IllegalArgumentException if the initial capacity of
- * elements is negative or the load factor is nonpositive
- * @since 1.6
- */
- public SafeConcurrentHashMap(int initialCapacity, float loadFactor) {
- super(initialCapacity, loadFactor);
- }
-
- /**
- * Creates a new, empty map with an initial table size based on
- * the given number of elements ({@code initialCapacity}), table
- * density ({@code loadFactor}), and number of concurrently
- * updating threads ({@code concurrencyLevel}).
- *
- * @param initialCapacity the initial capacity. The implementation
- * performs internal sizing to accommodate this many elements,
- * given the specified load factor.
- * @param loadFactor the load factor (table density) for
- * establishing the initial table size
- * @param concurrencyLevel the estimated number of concurrently
- * updating threads. The implementation may use this value as
- * a sizing hint.
- * @throws IllegalArgumentException if the initial capacity is
- * negative or the load factor or concurrencyLevel are
- * nonpositive
- */
- public SafeConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
- super(initialCapacity, loadFactor, concurrencyLevel);
- }
-
- /** {@inheritDoc} */
- @Override
- public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
- return JRE.isJava8()
- ? ConcurrentHashMapTools.computeIfAbsentForJava8(this, key, mappingFunction)
- : super.computeIfAbsent(key, mappingFunction);
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java b/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java
index aa434c6..4d8e03b 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/constant/PatternConsts.java
@@ -22,23 +22,76 @@ import java.util.regex.Pattern;
* 正则表达式常量
*
* @author ZhouXY
+ * @see RegexConsts
+ * @see xyz.zhouxy.plusone.commons.util.RegexTools
*/
public final class PatternConsts {
- public static final Pattern DATE = Pattern.compile(RegexConsts.DATE);
+ /**
+ * yyyyMMdd
+ *
+ * @see RegexConsts#BASIC_ISO_DATE
+ *
+ */
+ public static final Pattern BASIC_ISO_DATE = Pattern.compile(RegexConsts.BASIC_ISO_DATE);
+ /**
+ * yyyy-MM-dd
+ *
+ * @see RegexConsts#ISO_LOCAL_DATE
+ */
+ public static final Pattern ISO_LOCAL_DATE = Pattern.compile(RegexConsts.ISO_LOCAL_DATE);
+
+ /**
+ * 密码
+ *
+ * @see RegexConsts#PASSWORD
+ */
public static final Pattern PASSWORD = Pattern.compile(RegexConsts.PASSWORD);
+ /**
+ * 验证码
+ *
+ * @see RegexConsts#CAPTCHA
+ */
public static final Pattern CAPTCHA = Pattern.compile(RegexConsts.CAPTCHA);
+ /**
+ * 邮箱地址
+ *
+ * @see RegexConsts#EMAIL
+ */
public static final Pattern EMAIL = Pattern.compile(RegexConsts.EMAIL);
+ /**
+ * 中国大陆手机号
+ *
+ * @see RegexConsts#MOBILE_PHONE
+ */
public static final Pattern MOBILE_PHONE = Pattern.compile(RegexConsts.MOBILE_PHONE);
+ /**
+ * 用户名
+ *
+ * @see RegexConsts#USERNAME
+ */
public static final Pattern USERNAME = Pattern.compile(RegexConsts.USERNAME);
+ /**
+ * 昵称
+ *
+ * @see RegexConsts#NICKNAME
+ */
public static final Pattern NICKNAME = Pattern.compile(RegexConsts.NICKNAME);
+ /**
+ * 中国第二代居民身份证
+ *
+ * @see RegexConsts#CHINESE_2ND_ID_CARD_NUMBER
+ */
+ public static final Pattern CHINESE_2ND_ID_CARD_NUMBER
+ = Pattern.compile(RegexConsts.CHINESE_2ND_ID_CARD_NUMBER, Pattern.CASE_INSENSITIVE);
+
private PatternConsts() {
throw new IllegalStateException("Utility class");
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java b/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java
index 64e3bea..9730ce1 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/constant/RegexConsts.java
@@ -20,22 +20,30 @@ package xyz.zhouxy.plusone.commons.constant;
* 正则表达式常量
*
* @author ZhouXY
+ * @see PatternConsts
*/
public final class RegexConsts {
- public static final String DATE = "^\\d{4}-\\d{2}-\\d{2}";
+ public static final String BASIC_ISO_DATE = "^(?\\d{4,9})(?\\d{2})(?\\d{2})";
+
+ public static final String ISO_LOCAL_DATE = "^(?\\d{4,9})-(?\\d{2})-(?\\d{2})";
public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])[\\w\\\\!#$%&'*\\+\\-/=?^`{|}~@\\(\\)\\[\\]\",\\.;':><]{8,32}$";
- public static final String CAPTCHA = "^[0-9A-Za-z]{4,6}$";
+ public static final String CAPTCHA = "^\\w{4,6}$";
- public static final String EMAIL = "^\\w+([-+.]\\w+)*@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})*(\\.(?![0-9]+$)[a-zA-Z0-9][-0-9A-Za-z]{0,62})$";
+ 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])+)\\])";
public static final String MOBILE_PHONE = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$";
- public static final String USERNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$";
+ public static final String USERNAME = "^[\\w-_.@]{4,36}$";
- public static final String NICKNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$";
+ public static final String NICKNAME = "^[\\w-_.@]{4,36}$";
+
+ public static final String CHINESE_2ND_ID_CARD_NUMBER
+ = "^(?(?(?\\d{2})\\d{2})\\d{2})(?\\d{8})\\d{2}(?\\d)([\\dX])$";
private RegexConsts() {
throw new IllegalStateException("Utility class");
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/DataNotExistsException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/DataNotExistsException.java
new file mode 100644
index 0000000..5877a68
--- /dev/null
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/DataNotExistsException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 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.exception;
+
+/**
+ * 数据不存在异常
+ *
+ * @author ZhouXY
+ * @since 0.1.0
+ */
+public final class DataNotExistsException extends Exception {
+
+ private static final long serialVersionUID = 6536955800679703111L;
+
+ public DataNotExistsException() {
+ super();
+ }
+
+ public DataNotExistsException(String message) {
+ super(message);
+ }
+
+ public DataNotExistsException(Throwable cause) {
+ super(cause);
+ }
+
+ public DataNotExistsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/ExceptionType.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/ExceptionType.java
new file mode 100644
index 0000000..f8ad83b
--- /dev/null
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/ExceptionType.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2024 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.exception;
+
+import javax.annotation.Nonnull;
+
+import xyz.zhouxy.plusone.commons.base.IWithCode;
+
+/**
+ * 异常类型
+ *
+ *
+ * 异常在不同场景下被抛出,可以用不同的枚举值,表示不同的异常类型。
+ * 该枚举实现本接口,用于基于不同类型创建异常。
+ *
+ *
+ * public final class LoginException extends RuntimeException {
+ * private final Type type;
+ * private LoginException(Type type, String message) {
+ * super(message);
+ * this.type = type;
+ * }
+ *
+ * private LoginException(Type type, Throwable cause) {
+ * super(cause);
+ * this.type = type;
+ * }
+ *
+ * private LoginException(Type type, String message, Throwable cause) {
+ * super(message, cause);
+ * this.type = 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(String code, String defaultMessage) {
+ * this.code = code;
+ * this.defaultMessage = defaultMessage;
+ * }
+ *
+ * @Override
+ * @Nonnull
+ * public String getCode() {
+ * return code;
+ * }
+ *
+ * @Override
+ * public String getDefaultMessage() {
+ * return defaultMessage;
+ * }
+ *
+ * @Override
+ * @Nonnull
+ * public LoginException create() {
+ * return new LoginException(this, this.defaultMessage);
+ * }
+ *
+ * @Override
+ * @Nonnull
+ * public LoginException create(String message) {
+ * return new LoginException(this, message);
+ * }
+ *
+ * @Override
+ * @Nonnull
+ * public LoginException create(Throwable cause) {
+ * return new LoginException(this, cause);
+ * }
+ *
+ * @Override
+ * @Nonnull
+ * public LoginException create(String message, Throwable cause) {
+ * return new LoginException(this, message, cause);
+ * }
+ * }
+ * }
+ *
+ *
+ * 使用时,可以使用这种方式创建并抛出异常:
+ *
+ * throw LoginException.Type.TOKEN_TIMEOUT.create();
+ *
+ * |
+ *
+ * @author ZhouXY
+ */
+public interface ExceptionType extends IWithCode {
+
+ String getDefaultMessage();
+
+ @Nonnull
+ E create();
+
+ @Nonnull
+ E create(String message);
+
+ @Nonnull
+ E create(Throwable cause);
+
+ @Nonnull
+ E create(String message, Throwable cause);
+
+}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/ParsingFailureException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/ParsingFailureException.java
index 5f9ae68..0a86545 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/exception/ParsingFailureException.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/ParsingFailureException.java
@@ -18,13 +18,20 @@ package xyz.zhouxy.plusone.commons.exception;
import java.time.format.DateTimeParseException;
+import javax.annotation.Nonnull;
+
+import xyz.zhouxy.plusone.commons.exception.business.RequestParamsException;
+
/**
* 解析失败异常
*
*
- * 解析失败的不一定是客户传的参数,也可能是其它来源的数据解析失败
- * 如果表示用户传参造成的解析失败,可使用 RequestParamsException(Throwable cause),
- * 将 ParsingFailureException 包装成 {@link RequestParamsException} 再抛出
+ * 解析失败的不一定是客户传的参数,也可能是其它来源的数据解析失败。
+ * 如果表示用户传参造成的解析失败,可使用 {@link RequestParamsException#RequestParamsException(Throwable)},
+ * 将 ParsingFailureException 包装成 {@link RequestParamsException} 再抛出。
+ *
+ * throw new RequestParamsException(ParsingFailureException.of(ParsingFailureException.Type.NUMBER_PARSING_FAILURE));
+ *
*
*
* @author ZhouXY
@@ -34,13 +41,8 @@ public final class ParsingFailureException extends RuntimeException {
private final Type type;
- private ParsingFailureException(Type type) {
- super(type.getDefaultMsg());
- this.type = type;
- }
-
- private ParsingFailureException(Type type, String msg) {
- super(msg);
+ private ParsingFailureException(Type type, String message) {
+ super(message);
this.type = type;
}
@@ -49,61 +51,114 @@ public final class ParsingFailureException extends RuntimeException {
this.type = type;
}
- private ParsingFailureException(Type type, String msg, Throwable cause) {
- super(msg, cause);
+ private ParsingFailureException(Type type, String message, Throwable cause) {
+ super(message, cause);
this.type = type;
}
- public static ParsingFailureException of(Type type) {
- return new ParsingFailureException(type);
+ public ParsingFailureException() {
+ this(Type.DEFAULT, Type.DEFAULT.getDefaultMessage());
}
- public static ParsingFailureException of(Type type, String msg) {
- return new ParsingFailureException(type, msg);
+ public ParsingFailureException(String message) {
+ this(Type.DEFAULT, message);
}
- public static ParsingFailureException of(Type type, Throwable e) {
- return new ParsingFailureException(type, e);
+ public ParsingFailureException(Throwable cause) {
+ this(Type.DEFAULT, cause);
}
- public static ParsingFailureException of(Type type, String msg, Throwable e) {
- return new ParsingFailureException(type, msg, e);
+ public ParsingFailureException(String message, Throwable cause) {
+ this(Type.DEFAULT, message, cause);
}
- public static ParsingFailureException of(DateTimeParseException e) {
- return new ParsingFailureException(Type.DATE_TIME_PARSING_FAILURE, e.getMessage(), e);
+ public static ParsingFailureException of(DateTimeParseException cause) {
+ if (cause == null) {
+ return Type.DATE_TIME_PARSING_FAILURE.create();
+ }
+ return Type.DATE_TIME_PARSING_FAILURE.create(cause.getMessage(), cause);
}
- public static ParsingFailureException of(String msg, DateTimeParseException e) {
- return new ParsingFailureException(Type.DATE_TIME_PARSING_FAILURE, msg, e);
+ public static ParsingFailureException of(String message, DateTimeParseException cause) {
+ return Type.DATE_TIME_PARSING_FAILURE.create(message, cause);
+ }
+
+ public static ParsingFailureException of(NumberFormatException cause) {
+ if (cause == null) {
+ return Type.NUMBER_PARSING_FAILURE.create();
+ }
+ return Type.NUMBER_PARSING_FAILURE.create(cause.getMessage(), cause);
+ }
+
+ public static ParsingFailureException of(String message, NumberFormatException cause) {
+ return Type.NUMBER_PARSING_FAILURE.create(message, cause);
}
public Type getType() {
return type;
}
- public enum Type {
- DEFAULT("4010500", "解析失败"),
- NUMBER_PARSING_FAILURE("4010501", "数字转换失败"),
- DATE_TIME_PARSING_FAILURE("4010502", "时间解析失败"),
- JSON_PARSING_FAILURE("4010503", "JSON 解析失败"),
- XML_PARSING_FAILURE("4010504", "XML 解析失败"),
+ public String getCode() {
+ return this.type.code;
+ }
+
+ public static final Type DEFAULT = Type.DEFAULT;
+ public static final Type NUMBER_PARSING_FAILURE = Type.NUMBER_PARSING_FAILURE;
+ public static final Type DATE_TIME_PARSING_FAILURE = Type.DATE_TIME_PARSING_FAILURE;
+ public static final Type JSON_PARSING_FAILURE = Type.JSON_PARSING_FAILURE;
+ public static final Type XML_PARSING_FAILURE = Type.XML_PARSING_FAILURE;
+
+ public enum Type implements ExceptionType {
+ DEFAULT("00", "解析失败"),
+ NUMBER_PARSING_FAILURE("10", "数字转换失败"),
+ DATE_TIME_PARSING_FAILURE("20", "时间解析失败"),
+ JSON_PARSING_FAILURE("30", "JSON 解析失败"),
+ XML_PARSING_FAILURE("40", "XML 解析失败"),
;
- final String code;
- final String defaultMsg;
+ @Nonnull
+ private final String code;
+ @Nonnull
+ private final String defaultMessage;
- Type(String code, String defaultMsg) {
+ Type(String code, String defaultMessage) {
this.code = code;
- this.defaultMsg = defaultMsg;
+ this.defaultMessage = defaultMessage;
}
+ @Override
+ @Nonnull
public String getCode() {
return code;
}
- public String getDefaultMsg() {
- return defaultMsg;
+ @Override
+ public String getDefaultMessage() {
+ return defaultMessage;
+ }
+
+ @Override
+ @Nonnull
+ public ParsingFailureException create() {
+ return new ParsingFailureException(this, this.defaultMessage);
+ }
+
+ @Override
+ @Nonnull
+ public ParsingFailureException create(String message) {
+ return new ParsingFailureException(this, message);
+ }
+
+ @Override
+ @Nonnull
+ public ParsingFailureException create(Throwable cause) {
+ return new ParsingFailureException(this, cause);
+ }
+
+ @Override
+ @Nonnull
+ public ParsingFailureException create(String message, Throwable cause) {
+ return new ParsingFailureException(this, message, cause);
}
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/business/BizException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/business/BizException.java
index eee5262..ed4b664 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/exception/business/BizException.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/business/BizException.java
@@ -37,16 +37,16 @@ public class BizException extends RuntimeException {
super(DEFAULT_MSG);
}
- public BizException(String msg) {
- super(msg);
+ public BizException(String message) {
+ super(message);
}
public BizException(Throwable cause) {
super(cause);
}
- public BizException(String msg, Throwable cause) {
- super(msg, cause);
+ public BizException(String message, Throwable cause) {
+ super(message, cause);
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/business/InvalidInputException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/business/InvalidInputException.java
index 8b8495c..aacf7a6 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/exception/business/InvalidInputException.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/business/InvalidInputException.java
@@ -16,6 +16,10 @@
package xyz.zhouxy.plusone.commons.exception.business;
+import javax.annotation.Nonnull;
+
+import xyz.zhouxy.plusone.commons.exception.ExceptionType;
+
/**
* InvalidInputException
*
@@ -34,12 +38,12 @@ public final class InvalidInputException extends RequestParamsException {
private final Type type;
private InvalidInputException(Type type) {
- super(type.getDefaultMsg());
+ super(type.getDefaultMessage());
this.type = type;
}
- private InvalidInputException(Type type, String msg) {
- super(msg);
+ private InvalidInputException(Type type, String message) {
+ super(message);
this.type = type;
}
@@ -48,40 +52,36 @@ public final class InvalidInputException extends RequestParamsException {
this.type = type;
}
- private InvalidInputException(Type type, String msg, Throwable cause) {
- super(msg, cause);
+ private InvalidInputException(Type type, String message, Throwable cause) {
+ super(message, cause);
this.type = type;
}
- public static InvalidInputException of(Type type) {
- return new InvalidInputException(type);
+ public InvalidInputException() {
+ this(Type.DEFAULT);
}
- public static InvalidInputException of(Type type, String msg) {
- return new InvalidInputException(type, msg);
+ public InvalidInputException(String message) {
+ this(Type.DEFAULT, message);
}
- public static InvalidInputException of(Type type, Throwable e) {
- return new InvalidInputException(type, e);
+ public InvalidInputException(Throwable cause) {
+ this(Type.DEFAULT, cause);
}
- public static InvalidInputException of(Type type, String msg, Throwable e) {
- return new InvalidInputException(type, msg, e);
- }
-
- public static InvalidInputException of(Throwable e) {
- return new InvalidInputException(Type.DEFAULT, e.getMessage(), e);
- }
-
- public static InvalidInputException of(String msg, Throwable e) {
- return new InvalidInputException(Type.DEFAULT, msg, e);
+ public InvalidInputException(String message, Throwable cause) {
+ this(Type.DEFAULT, message, cause);
}
public Type getType() {
- return type;
+ return this.type;
}
- public enum Type {
+ public Object getCode() {
+ return this.type.code;
+ }
+
+ public enum Type implements ExceptionType {
DEFAULT("00", "用户输入内容非法"),
CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS("01", "包含非法恶意跳转链接"),
CONTAINS_ILLEGAL_WORDS("02", "包含违禁敏感词"),
@@ -89,20 +89,49 @@ public final class InvalidInputException extends RequestParamsException {
INFRINGE_COPYRIGHT("04", "文件侵犯版权"),
;
+ @Nonnull
final String code;
- final String defaultMsg;
+ @Nonnull
+ final String defaultMessage;
Type(String code, String defaultMsg) {
this.code = code;
- this.defaultMsg = defaultMsg;
+ this.defaultMessage = defaultMsg;
}
+ @Override
+ @Nonnull
public String getCode() {
return code;
}
- public String getDefaultMsg() {
- return defaultMsg;
+ @Override
+ public String getDefaultMessage() {
+ return defaultMessage;
+ }
+
+ @Override
+ @Nonnull
+ public InvalidInputException create() {
+ return new InvalidInputException(this);
+ }
+
+ @Override
+ @Nonnull
+ public InvalidInputException create(String message) {
+ return new InvalidInputException(this, message);
+ }
+
+ @Override
+ @Nonnull
+ public InvalidInputException create(Throwable cause) {
+ return new InvalidInputException(this, cause);
+ }
+
+ @Override
+ @Nonnull
+ public InvalidInputException create(String message, Throwable cause) {
+ return new InvalidInputException(this, message, cause);
}
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/business/RequestParamsException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/business/RequestParamsException.java
index 8f4f383..4d9155a 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/exception/business/RequestParamsException.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/business/RequestParamsException.java
@@ -34,16 +34,16 @@ public class RequestParamsException extends BizException {
super(DEFAULT_MSG);
}
- public RequestParamsException(String msg) {
- super(msg);
+ public RequestParamsException(String message) {
+ super(message);
}
public RequestParamsException(Throwable cause) {
super(cause);
}
- public RequestParamsException(String msg, Throwable cause) {
- super(msg, cause);
+ public RequestParamsException(String message, Throwable cause) {
+ super(message, cause);
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/system/DataOperationResultException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/system/DataOperationResultException.java
index 99db690..bbb49f6 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/exception/system/DataOperationResultException.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/system/DataOperationResultException.java
@@ -39,15 +39,15 @@ public final class DataOperationResultException extends SysException {
super(DEFAULT_MSG);
}
- public DataOperationResultException(String msg) {
- super(msg);
+ public DataOperationResultException(String message) {
+ super(message);
}
public DataOperationResultException(Throwable cause) {
super(cause);
}
- public DataOperationResultException(String msg, Throwable cause) {
- super(msg, cause);
+ public DataOperationResultException(String message, Throwable cause) {
+ super(message, cause);
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/system/NoAvailableMacFoundException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/system/NoAvailableMacFoundException.java
index 7897b46..d7c76f8 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/exception/system/NoAvailableMacFoundException.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/system/NoAvailableMacFoundException.java
@@ -33,15 +33,15 @@ public class NoAvailableMacFoundException extends SysException {
super();
}
- public NoAvailableMacFoundException(String msg) {
- super(msg);
+ public NoAvailableMacFoundException(String message) {
+ super(message);
}
- public NoAvailableMacFoundException(Throwable e) {
- super(e);
+ public NoAvailableMacFoundException(Throwable cause) {
+ super(cause);
}
- public NoAvailableMacFoundException(String msg, Throwable e) {
- super(msg, e);
+ public NoAvailableMacFoundException(String message, Throwable cause) {
+ super(message, cause);
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/exception/system/SysException.java b/src/main/java/xyz/zhouxy/plusone/commons/exception/system/SysException.java
index 877d43a..7480bd6 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/exception/system/SysException.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/exception/system/SysException.java
@@ -30,19 +30,19 @@ public class SysException extends RuntimeException {
private static final String DEFAULT_MSG = "系统异常";
- protected SysException() {
+ public SysException() {
super(DEFAULT_MSG);
}
- public SysException(String msg) {
- super(msg);
+ public SysException(String message) {
+ super(message);
}
public SysException(Throwable cause) {
super(cause);
}
- public SysException(String msg, Throwable cause) {
- super(msg, cause);
+ public SysException(String message, Throwable cause) {
+ super(message, cause);
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/function/ThrowingFunction.java b/src/main/java/xyz/zhouxy/plusone/commons/function/ThrowingFunction.java
new file mode 100644
index 0000000..08ed960
--- /dev/null
+++ b/src/main/java/xyz/zhouxy/plusone/commons/function/ThrowingFunction.java
@@ -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.
+ */
+package xyz.zhouxy.plusone.commons.function;
+
+@FunctionalInterface
+public interface ThrowingFunction {
+
+ /**
+ * Applies this function to the given argument.
+ *
+ * @param t the function argument
+ * @return the function result
+ */
+ R apply(T t) throws E;
+
+}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/function/package-info.java b/src/main/java/xyz/zhouxy/plusone/commons/function/package-info.java
new file mode 100644
index 0000000..2c731f4
--- /dev/null
+++ b/src/main/java/xyz/zhouxy/plusone/commons/function/package-info.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+/**
+ * 函数式编程
+ *
+ * PredicateTools
+ *
+ * {@link PredicateTools} 用于 {@link java.util.function.Predicate} 的相关操作。
+ *
+ *
+ * Functional interfaces
+ *
+ * 补充一些 JDK 没有,而项目中可能用得上的函数式接口:
+ *
+ * | 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) |
+ *
+ *
+ */
+package xyz.zhouxy.plusone.commons.function;
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/math/Interval.java b/src/main/java/xyz/zhouxy/plusone/commons/math/Interval.java
deleted file mode 100644
index aed22e9..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/math/Interval.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2024 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.math;
-
-import java.util.Optional;
-
-import javax.annotation.Nonnull;
-
-import com.google.common.base.Preconditions;
-import com.google.errorprone.annotations.Immutable;
-
-import xyz.zhouxy.plusone.commons.util.Numbers;
-
-@Immutable
-public class Interval> {
- @Nonnull
- private final IntervalType intervalType;
- private final T lowerBound;
- private final T upperBound;
-
- public Interval(@Nonnull IntervalType intervalType, T lowerBound, T upperBound) {
- Preconditions.checkNotNull(intervalType);
- if (intervalType.isLeftClosed()) {
- Preconditions.checkArgument(lowerBound != null,
- "The lower bound cannot be null, when the interval is left-closed.");
- }
- if (intervalType.isRightClosed()) {
- Preconditions.checkArgument(upperBound != null,
- "The upper bound cannot be null, when the interval is right-closed.");
- }
- if (lowerBound != null && upperBound != null) {
- Preconditions.checkArgument(lowerBound.compareTo(upperBound) <= 0,
- "The lower bound must less than the upper bound.");
- }
- this.intervalType = intervalType;
- this.lowerBound = lowerBound;
- this.upperBound = upperBound;
- }
-
- @Nonnull
- public final IntervalType getIntervalType() {
- return intervalType;
- }
-
- @Nonnull
- public final Optional getLowerBound() {
- return Optional.ofNullable(lowerBound);
- }
-
- @Nonnull
- public final Optional getUpperBound() {
- return Optional.ofNullable(upperBound);
- }
-
- public final boolean isLeftClosed() {
- return this.intervalType.isLeftClosed();
- }
-
- public final boolean isRightClosed() {
- return this.intervalType.isRightClosed();
- }
-
- public final boolean validValue(@Nonnull T value) {
- return Numbers.between(value, this.lowerBound, this.upperBound, this.intervalType);
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/math/IntervalType.java b/src/main/java/xyz/zhouxy/plusone/commons/math/IntervalType.java
deleted file mode 100644
index 1127820..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/math/IntervalType.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2024 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.math;
-
-public enum IntervalType {
- /** 开区间。(a,b)={x|a < x < b} */
- OPEN(false, false),
- /** 闭区间。[a,b]={x|a ≤ x ≤ b} */
- CLOSED(true, true),
- /** 左闭右开区间。[a,b)={x|a ≤ x < b} */
- CLOSED_OPEN(true, false),
- /** 左开右闭区间。(a,b]={x|a < x ≤ b} */
- OPEN_CLOSED(false, true);
-
- private final boolean leftClosed;
- private final boolean rightClosed;
-
- IntervalType(boolean leftClosed, boolean rightClosed) {
- this.leftClosed = leftClosed;
- this.rightClosed = rightClosed;
- }
-
- public final boolean isLeftClosed() {
- return leftClosed;
- }
-
- public final boolean isRightClosed() {
- return rightClosed;
- }
-
- public final > Interval buildInterval(T left, T right) {
- return new Interval<>(this, left, right);
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java b/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java
index c6ad7a7..f802468 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumber.java
@@ -18,18 +18,35 @@ package xyz.zhouxy.plusone.commons.model;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
import java.util.Map;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.Immutable;
+
+import xyz.zhouxy.plusone.commons.annotation.ReaderMethod;
+import xyz.zhouxy.plusone.commons.annotation.ValueObject;
+import xyz.zhouxy.plusone.commons.constant.PatternConsts;
+import xyz.zhouxy.plusone.commons.util.AssertTools;
+import xyz.zhouxy.plusone.commons.util.StringTools;
/**
+ * Chinese2ndGenIDCardNumber
+ *
+ *
* 中国第二代居民身份证号
+ *
+ *
+ * @author ZhouXY
+ * @since 1.0
+ * @see xyz.zhouxy.plusone.commons.constant.PatternConsts#CHINESE_2ND_ID_CARD_NUMBER
*/
-public class Chinese2ndGenIDCardNumber extends IDCardNumber {
+@ValueObject
+@Immutable
+public class Chinese2ndGenIDCardNumber
+ extends ValidatableStringRecord
+ implements IDCardNumber {
/** 省份编码 */
private final String provinceCode;
@@ -42,83 +59,101 @@ public class Chinese2ndGenIDCardNumber extends IDCardNumber {
/** 出生日期 */
private final LocalDate birthDate;
- public static final Pattern PATTERN = Pattern.compile("^(((\\d{2})\\d{2})\\d{2})(\\d{8})\\d{2}(\\d)([\\dXx])$");
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
- private Chinese2ndGenIDCardNumber(String idNumber) {
- super(idNumber, PATTERN, "Invalid ID number");
+ private Chinese2ndGenIDCardNumber(String value) {
+ super(value.toUpperCase(), PatternConsts.CHINESE_2ND_ID_CARD_NUMBER, () -> "二代居民身份证校验失败:" + value);
+
+ final Matcher matcher = getMatcher();
+
+ final String provinceCodeValue = matcher.group("province");
+ AssertTools.checkArgument(Chinese2ndGenIDCardNumber.PROVINCE_CODES.containsKey(provinceCodeValue));
+
+ final String cityCodeValue = matcher.group("city");
+ final String countyCodeValue = matcher.group("county");
+
+ final Gender genderValue;
+ final LocalDate birthDateValue;
try {
- final Matcher matcher = getMatcher();
- this.provinceCode = matcher.group(3);
- this.cityCode = matcher.group(2);
- this.countyCode = matcher.group(1);
+ // 出生日期
+ final String birthDateStr = matcher.group("birthDate");
+ birthDateValue = LocalDate.parse(birthDateStr, DATE_FORMATTER);
// 性别
- final String genderStr = matcher.group(5);
- final int genderIndex = Integer.parseInt(genderStr);
- this.gender = genderIndex % 2 == 0 ? Gender.FEMALE : Gender.MALE;
-
- // 出生日期
- final String birthDateStr = matcher.group(4);
- this.birthDate = LocalDate.parse(birthDateStr, DATE_FORMATTER);
+ final int genderCode = Integer.parseInt(matcher.group("gender"));
+ genderValue = genderCode % 2 == 0 ? Gender.FEMALE : Gender.MALE;
}
- catch (DateTimeParseException e) {
+ catch (Exception e) {
throw new IllegalArgumentException(e);
}
+
+ this.provinceCode = provinceCodeValue;
+ this.cityCode = cityCodeValue;
+ this.countyCode = countyCodeValue;
+ this.gender = genderValue;
+ this.birthDate = birthDateValue;
}
- public static Chinese2ndGenIDCardNumber of(String idNumber) {
- return new Chinese2ndGenIDCardNumber(idNumber);
+ public static Chinese2ndGenIDCardNumber of(final String value) {
+ AssertTools.checkArgument(StringTools.isNotBlank(value), "二代居民身份证校验失败:号码为空");
+ return new Chinese2ndGenIDCardNumber(value);
}
+ // ================================
+ // #region - reader methods
+ // ================================
+
+ @ReaderMethod
public String getProvinceCode() {
return provinceCode;
}
+ @ReaderMethod
public String getProvinceName() {
return PROVINCE_CODES.get(this.provinceCode);
}
+ @ReaderMethod
public String getFullProvinceCode() {
return Strings.padEnd(this.provinceCode, 12, '0');
}
+ @ReaderMethod
public String getCityCode() {
return cityCode;
}
+ @ReaderMethod
public String getFullCityCode() {
return Strings.padEnd(this.cityCode, 12, '0');
}
+ @ReaderMethod
public String getCountyCode() {
return countyCode;
}
+ @ReaderMethod
public String getFullCountyCode() {
return Strings.padEnd(this.countyCode, 12, '0');
}
+ @ReaderMethod
@Override
public Gender getGender() {
return gender;
}
+ @ReaderMethod
@Override
public LocalDate getBirthDate() {
return birthDate;
}
- @Override
- public int hashCode() {
- return super.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return super.equals(obj);
- }
+ // ================================
+ // #endregion - reader methods
+ // ================================
/**
* 省份代码表
@@ -165,4 +200,14 @@ public class Chinese2ndGenIDCardNumber extends IDCardNumber {
.put("91", "国外")
.build();
}
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/Gender.java b/src/main/java/xyz/zhouxy/plusone/commons/model/Gender.java
index 24dbeb9..7922305 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/model/Gender.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/model/Gender.java
@@ -16,6 +16,7 @@
package xyz.zhouxy.plusone.commons.model;
+import xyz.zhouxy.plusone.commons.base.IWithIntCode;
import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
@@ -23,7 +24,7 @@ import xyz.zhouxy.plusone.commons.util.AssertTools;
*
* @author ZhouXY
*/
-public enum Gender {
+public enum Gender implements IWithIntCode {
UNKNOWN(0, "Unknown", "未知"),
MALE(1, "Male", "男"),
FEMALE(2, "Female", "女"),
@@ -60,4 +61,9 @@ public enum Gender {
return displayNameZh;
}
+ @Override
+ public int getCode() {
+ return getValue();
+ }
+
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java b/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java
index f6e3114..3e7c56d 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/model/IDCardNumber.java
@@ -18,44 +18,60 @@ package xyz.zhouxy.plusone.commons.model;
import java.time.LocalDate;
import java.time.Period;
-import java.util.function.Supplier;
-import java.util.regex.Pattern;
-import javax.annotation.Nonnull;
+import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
* 身份证号
*/
-public abstract class IDCardNumber extends ValidatableStringRecord {
+public interface IDCardNumber {
- protected IDCardNumber(@Nonnull String idNumber, @Nonnull Pattern pattern)
- throws IllegalArgumentException{
- super(idNumber, pattern);
- }
+ static final char DEFAULT_REPLACED_CHAR = '*';
+ static final int DEFAULT_DISPLAY_FRONT = 1;
+ static final int DEFAULT_DISPLAY_END = 2;
- protected IDCardNumber(@Nonnull String idNumber, @Nonnull Pattern pattern,
- @Nonnull String errorMessage) {
- super(idNumber, pattern, errorMessage);
- }
-
- protected IDCardNumber(@Nonnull String idNumber, @Nonnull Pattern pattern,
- @Nonnull Supplier errorMessage) {
- super(idNumber, pattern, errorMessage);
- }
+ String value();
/**
* 根据身份证号判断性别
*/
- public abstract Gender getGender();
+ Gender getGender();
/**
* 获取出生日期
*/
- public abstract LocalDate getBirthDate();
+ LocalDate getBirthDate();
/** 计算年龄 */
- public final int calculateAge() {
+ default int getAge() {
LocalDate now = LocalDate.now();
return Period.between(getBirthDate(), now).getYears();
}
+
+ // ================================
+ // #region - toString
+ // ================================
+
+ default String toDesensitizedString() {
+ return toDesensitizedString(DEFAULT_REPLACED_CHAR, DEFAULT_DISPLAY_FRONT, DEFAULT_DISPLAY_END);
+ }
+
+ default String toDesensitizedString(int front, int end) {
+ return toDesensitizedString(DEFAULT_REPLACED_CHAR, front, end);
+ }
+
+ default String toDesensitizedString(char replacedChar, int front, int end) {
+ final String value = value();
+ AssertTools.checkArgument(front >= 0 && end >= 0);
+ AssertTools.checkArgument((front + end) <= value.length(), "需要截取的长度不能大于身份证号长度");
+ final char[] charArray = value.toCharArray();
+ for (int i = front; i < charArray.length - end; i++) {
+ charArray[i] = replacedChar;
+ }
+ return String.valueOf(charArray);
+ }
+
+ // ================================
+ // #endregion - toString
+ // ================================
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java b/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java
index fc63894..c679a14 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecord.java
@@ -23,6 +23,7 @@ import java.util.regex.Pattern;
import javax.annotation.Nonnull;
+import xyz.zhouxy.plusone.commons.annotation.ReaderMethod;
import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
@@ -31,8 +32,8 @@ import xyz.zhouxy.plusone.commons.util.AssertTools;
* @author ZhouXY
* @since 0.1.0
*/
-public abstract class ValidatableStringRecord
- implements Comparable {
+public abstract class ValidatableStringRecord>
+ implements Comparable {
@Nonnull
private final String value;
@@ -50,8 +51,8 @@ public abstract class ValidatableStringRecord
protected ValidatableStringRecord(@Nonnull String value, @Nonnull Pattern pattern,
@Nonnull String errorMessage) {
- AssertTools.checkArgumentNotNull(value, "The value cannot be null.");
- AssertTools.checkArgumentNotNull(pattern, "The pattern cannot be null.");
+ AssertTools.checkArgument(Objects.nonNull(value), "The value cannot be null.");
+ AssertTools.checkArgument(Objects.nonNull(pattern), "The pattern cannot be null.");
this.matcher = pattern.matcher(value);
AssertTools.checkArgument(this.matcher.matches(), errorMessage);
this.value = value;
@@ -62,18 +63,19 @@ public abstract class ValidatableStringRecord
*
* @return 字符串(不为空)
*/
+ @ReaderMethod
public final String value() {
return this.value;
}
@Override
- public int compareTo(ValidatableStringRecord o) {
- return this.value.compareTo(o.value);
+ public int compareTo(T o) {
+ return this.value.compareTo(o.value());
}
@Override
public int hashCode() {
- return Objects.hash(value);
+ return Objects.hash(getClass(), value);
}
@Override
@@ -84,8 +86,9 @@ public abstract class ValidatableStringRecord
return false;
if (getClass() != obj.getClass())
return false;
- ValidatableStringRecord other = (ValidatableStringRecord) obj;
- return Objects.equals(value, other.value);
+ @SuppressWarnings("unchecked")
+ T other = (T) obj;
+ return Objects.equals(value, other.value());
}
@Override
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java
index 3467182..353d86f 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PageResult.java
@@ -16,11 +16,11 @@
package xyz.zhouxy.plusone.commons.model.dto;
+import java.util.Collections;
import java.util.List;
-import com.google.common.base.Preconditions;
-
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
+import xyz.zhouxy.plusone.commons.collection.CollectionTools;
/**
* 返回分页查询的结果
@@ -37,8 +37,7 @@ public class PageResult {
private final List content;
private PageResult(List content, long total) {
- Preconditions.checkNotNull(content, "Content must not be null.");
- this.content = content;
+ this.content = CollectionTools.nullToEmptyList(content);
this.total = total;
}
@@ -47,6 +46,11 @@ public class PageResult {
return new PageResult<>(content, total);
}
+ @StaticFactoryMethod(PageResult.class)
+ public static PageResult empty() {
+ return new PageResult<>(Collections.emptyList(), 0L);
+ }
+
public long getTotal() {
return total;
}
@@ -57,6 +61,6 @@ public class PageResult {
@Override
public String toString() {
- return "PageDTO [total=" + total + ", content=" + content + "]";
+ return "PageResult [total=" + total + ", content=" + content + "]";
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PagingAndSortingQueryParams.java b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PagingAndSortingQueryParams.java
index 64f8db2..6eb0106 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PagingAndSortingQueryParams.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/PagingAndSortingQueryParams.java
@@ -21,12 +21,15 @@ import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import xyz.zhouxy.plusone.commons.annotation.Virtual;
+import xyz.zhouxy.plusone.commons.collection.CollectionTools;
+import xyz.zhouxy.plusone.commons.util.AssertTools;
+import xyz.zhouxy.plusone.commons.util.RegexTools;
import xyz.zhouxy.plusone.commons.util.StringTools;
/**
@@ -48,15 +51,15 @@ public class PagingAndSortingQueryParams {
private Long pageNum;
private List orderBy;
- private static final Pattern sortStrPattern = Pattern.compile("^[a-zA-Z]\\w+-(desc|asc|DESC|ASC)$");
+ private static final Pattern SORT_STR_PATTERN = Pattern.compile("^[a-zA-Z]\\w+-(desc|asc|DESC|ASC)$");
private final Map sortableProperties;
- public PagingAndSortingQueryParams(Map sortableProperties) {
- Preconditions.checkArgument(sortableProperties != null && !sortableProperties.isEmpty(),
+ public PagingAndSortingQueryParams(@Nonnull Map sortableProperties) {
+ AssertTools.checkArgument(CollectionTools.isNotEmpty(sortableProperties),
"Sortable properties can not be empty.");
sortableProperties.forEach((k, v) ->
- Preconditions.checkArgument(StringTools.isNotBlank(k) && StringTools.isNotBlank(v),
+ AssertTools.checkArgument(StringTools.isNotBlank(k) && StringTools.isNotBlank(v),
"Property name must not be blank."));
this.sortableProperties = ImmutableMap.copyOf(sortableProperties);
}
@@ -101,12 +104,12 @@ public class PagingAndSortingQueryParams {
}
private SortableProperty generateSortableProperty(String orderByStr) {
- Preconditions.checkArgument(PagingAndSortingQueryParams.sortStrPattern.matcher(orderByStr).matches());
+ AssertTools.checkArgument(RegexTools.matches(orderByStr, SORT_STR_PATTERN));
String[] propertyNameAndOrderType = orderByStr.split("-");
- Preconditions.checkArgument(propertyNameAndOrderType.length == 2);
+ AssertTools.checkArgument(propertyNameAndOrderType.length == 2);
String propertyName = propertyNameAndOrderType[0];
- Preconditions.checkArgument(sortableProperties.containsKey(propertyName),
+ AssertTools.checkArgument(sortableProperties.containsKey(propertyName),
"The property name must be in the set of sortable properties.");
String columnName = sortableProperties.get(propertyName);
String orderType = propertyNameAndOrderType[1];
@@ -123,7 +126,7 @@ public class PagingAndSortingQueryParams {
SortableProperty(String propertyName, String columnName, String orderType) {
this.propertyName = propertyName;
this.columnName = columnName;
- Preconditions.checkArgument("ASC".equalsIgnoreCase(orderType) || "DESC".equalsIgnoreCase(orderType));
+ AssertTools.checkArgument("ASC".equalsIgnoreCase(orderType) || "DESC".equalsIgnoreCase(orderType));
this.orderType = orderType.toUpperCase();
this.sqlSnippet = this.propertyName + " " + this.orderType;
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponse.java b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponse.java
index 143fc86..d33d0df 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponse.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponse.java
@@ -22,7 +22,7 @@ import java.util.function.Supplier;
import javax.annotation.Nullable;
-import com.google.common.base.Preconditions;
+import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
* 统一结果,对返回给前端的数据进行封装。
@@ -78,16 +78,16 @@ public abstract class UnifiedResponse {
public static UnifiedResponse of(final boolean isSuccess,
final Supplier successResult, final Supplier errorResult) {
- Preconditions.checkNotNull(successResult, "Success supplier must not be null.");
- Preconditions.checkNotNull(errorResult, "Error supplier must not be null.");
+ AssertTools.checkNotNull(successResult, "Success supplier must not be null.");
+ AssertTools.checkNotNull(errorResult, "Error supplier must not be null.");
return isSuccess ? successResult.get() : errorResult.get();
}
public static UnifiedResponse of(final BooleanSupplier isSuccess,
final Supplier successResult, final Supplier errorResult) {
- Preconditions.checkNotNull(isSuccess, "Conditions for success must not be null.");
- Preconditions.checkNotNull(successResult, "Success supplier must not be null.");
- Preconditions.checkNotNull(errorResult, "Error supplier must not be null.");
+ AssertTools.checkNotNull(isSuccess, "Conditions for success must not be null.");
+ AssertTools.checkNotNull(successResult, "Success supplier must not be null.");
+ AssertTools.checkNotNull(errorResult, "Error supplier must not be null.");
return isSuccess.getAsBoolean() ? successResult.get() : errorResult.get();
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/sql/JdbcSql.java b/src/main/java/xyz/zhouxy/plusone/commons/sql/JdbcSql.java
deleted file mode 100644
index d24ed8a..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/sql/JdbcSql.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2023-2024 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.sql;
-
-import java.util.Collection;
-
-public class JdbcSql extends SQL {
-
- JdbcSql() {
- super();
- }
-
- public static JdbcSql newSql() {
- return new JdbcSql();
- }
-
- @Override
- public JdbcSql getSelf() {
- return this;
- }
-
- public static String IN(String col, Collection> c) {
- return IN(col, c.size());
- }
-
- public static String IN(String col, T[] c) {
- return IN(col, c.length);
- }
-
- private static String IN(String col, int length) {
- return col + " IN (" + String.valueOf(buildQuestionsList(length)) + ')';
- }
-
- public static String NOT_IN(String col, Collection> c) {
- return NOT_IN(col, c.size());
- }
-
- public static String NOT_IN(String col, T[] c) {
- return NOT_IN(col, c.length);
- }
-
- private static String NOT_IN(String col, int length) {
- return col + " NOT IN (" + String.valueOf(buildQuestionsList(length)) + ')';
- }
-
- private static char[] buildQuestionsList(int times) {
- char[] arr = new char[times * 3 - 2];
- int i = 0;
- for (int t = 1; t <= times; t++) {
- arr[i++] = '?';
- if (t < times) {
- arr[i++] = ',';
- arr[i++] = ' ';
- }
- }
- return arr;
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/sql/MyBatisSql.java b/src/main/java/xyz/zhouxy/plusone/commons/sql/MyBatisSql.java
deleted file mode 100644
index 3592205..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/sql/MyBatisSql.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2023-2024 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.sql;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public class MyBatisSql extends SQL {
-
- private final boolean withScript;
-
- MyBatisSql(boolean withScript) {
- super();
- this.withScript = withScript;
- }
-
- public static MyBatisSql newSql() {
- return new MyBatisSql(false);
- }
-
- public static MyBatisSql newScriptSql() {
- return new MyBatisSql(true);
- }
-
- @Override
- public MyBatisSql getSelf() {
- return this;
- }
-
- public static String IN(String col, String paramName) {
- return " " + col + " IN" + buildForeach(col, paramName);
- }
-
- public static String NOT_IN(String col, String paramName) {
- return col + " NOT IN" + buildForeach(col, paramName);
- }
-
- private static String buildForeach(String col, String paramName) {
- final String format = "" +
- "#{%s}" +
- " ";
- return String.format(format, col, paramName, col);
- }
-
- @Override
- public String toString() {
- if (withScript) {
- return "";
- }
- return super.toString();
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/sql/SQL.java b/src/main/java/xyz/zhouxy/plusone/commons/sql/SQL.java
deleted file mode 100644
index b0ef318..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/sql/SQL.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2023-2024 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.sql;
-
-import org.apache.ibatis.jdbc.AbstractSQL;
-
-import com.google.common.annotations.Beta;
-
-/**
- * @author ZhouXY
- */
-@Beta
-public abstract class SQL extends AbstractSQL {
-
- public static JdbcSql newJdbcSql() {
- return new JdbcSql();
- }
-
- public static MyBatisSql newMyBatisSql(boolean withScript) {
- return new MyBatisSql(withScript);
- }
-
- public T WHERE(boolean condition, String sqlCondition) {
- if (condition) {
- return WHERE(sqlCondition);
- }
- return getSelf();
- }
-
- public T WHERE(boolean condition, String ifSqlCondition, String elseSqlCondition) {
- return WHERE(condition ? ifSqlCondition : elseSqlCondition);
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java b/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java
index db2a5f6..279ca23 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/time/Quarter.java
@@ -16,20 +16,23 @@
package xyz.zhouxy.plusone.commons.time;
+import java.time.DateTimeException;
import java.time.Month;
import java.time.MonthDay;
+import java.time.temporal.ChronoField;
-import com.google.common.base.Preconditions;
+import com.google.common.collect.Range;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
-import xyz.zhouxy.plusone.commons.util.Numbers;
+import xyz.zhouxy.plusone.commons.base.IWithIntCode;
+import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
* 季度
*
* @author ZhouXY
*/
-public enum Quarter {
+public enum Quarter implements IWithIntCode {
/** 第一季度 */
Q1(1),
/** 第二季度 */
@@ -43,11 +46,7 @@ public enum Quarter {
/** 季度值 (1/2/3/4) */
private final int value;
- /** 季度开始月份 */
- private final int firstMonth;
-
- /** 季度结束月份 */
- private final int lastMonth;
+ private final Range monthRange;
/** 常量值 */
private static final Quarter[] ENUMS = Quarter.values();
@@ -58,8 +57,10 @@ public enum Quarter {
Quarter(int value) {
this.value = value;
- this.lastMonth = value * 3;
- this.firstMonth = this.lastMonth - 2;
+ final int lastMonth = value * 3;
+ final int firstMonth = lastMonth - 2;
+
+ this.monthRange = Range.closed(firstMonth, lastMonth);
}
// StaticFactoryMethods
@@ -73,7 +74,7 @@ public enum Quarter {
*/
@StaticFactoryMethod(Quarter.class)
public static Quarter fromMonth(int monthValue) {
- Preconditions.checkArgument(Numbers.between(monthValue, 1, 13), "Invalid value for MonthOfYear: " + monthValue);
+ ChronoField.MONTH_OF_YEAR.checkValidValue(monthValue);
return of(computeQuarterValueInternal(monthValue));
}
@@ -109,26 +110,23 @@ public enum Quarter {
*/
@StaticFactoryMethod(Quarter.class)
public static Quarter of(int value) {
- if (value < 1 || value > 4) {
- throw new IllegalArgumentException("Invalid value for Quarter: " + value);
- }
- return ENUMS[value - 1];
+ return ENUMS[checkValidIntValue(value) - 1];
}
// StaticFactoryMethods end
- // computs
+ // computes
- public Quarter plus(long quarters) { // TODO 单元测试
+ public Quarter plus(long quarters) {
final int amount = (int) ((quarters % 4) + 4);
return ENUMS[(ordinal() + amount) % 4];
}
- public Quarter minus(long quarters) { // TODO 单元测试
+ public Quarter minus(long quarters) {
return plus(-(quarters % 4));
}
- // computs end
+ // computes end
// Getters
@@ -136,24 +134,29 @@ public enum Quarter {
return value;
}
+ @Override
+ public int getCode() {
+ return getValue();
+ }
+
public Month firstMonth() {
- return Month.of(firstMonth);
+ return Month.of(firstMonthValue());
}
public int firstMonthValue() {
- return firstMonth;
+ return this.monthRange.lowerEndpoint();
}
public Month lastMonth() {
- return Month.of(lastMonth);
+ return Month.of(lastMonthValue());
}
public int lastMonthValue() {
- return lastMonth;
+ return this.monthRange.upperEndpoint();
}
public MonthDay firstMonthDay() {
- return MonthDay.of(this.firstMonth, 1);
+ return MonthDay.of(firstMonth(), 1);
}
public MonthDay lastMonthDay() {
@@ -163,11 +166,17 @@ public enum Quarter {
}
public int firstDayOfYear(boolean leapYear) {
- return Month.of(this.firstMonth).firstDayOfYear(leapYear);
+ return firstMonth().firstDayOfYear(leapYear);
}
// Getters end
+ public static int checkValidIntValue(int value) {
+ AssertTools.checkCondition(value >= 1 && value <= 4,
+ () -> new DateTimeException("Invalid value for Quarter: " + value));
+ return value;
+ }
+
// Internal
/**
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java b/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java
index 2925d99..947b065 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/time/YearQuarter.java
@@ -22,16 +22,17 @@ import java.io.Serializable;
import java.time.LocalDate;
import java.time.Month;
import java.time.YearMonth;
+import java.time.temporal.ChronoField;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import javax.annotation.Nonnull;
-import com.google.common.base.Preconditions;
import com.google.errorprone.annotations.Immutable;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
+import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
* 表示年份与季度
@@ -52,14 +53,13 @@ public final class YearQuarter implements Comparable, Serializable
private final LocalDate lastDate;
private YearQuarter(int year, @Nonnull Quarter quarter) {
- Preconditions.checkNotNull(quarter, "Quarter can not be null.");
this.year = year;
this.quarter = quarter;
this.firstDate = quarter.firstMonthDay().atYear(year);
this.lastDate = quarter.lastMonthDay().atYear(year);
}
- // #region - StaticFactoryMethod
+ // #region - StaticFactory
/**
* 根据指定年份与季度,创建 {@link YearQuarter} 实例
@@ -70,7 +70,7 @@ public final class YearQuarter implements Comparable, Serializable
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(int year, int quarter) {
- return of(year, Quarter.of(quarter));
+ return new YearQuarter(YEAR.checkValidIntValue(year), Quarter.of(quarter));
}
/**
@@ -81,8 +81,8 @@ public final class YearQuarter implements Comparable, Serializable
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
- public static YearQuarter of(int year, @Nonnull Quarter quarter) {
- return new YearQuarter(year, quarter);
+ public static YearQuarter of(int year, Quarter quarter) {
+ return new YearQuarter(YEAR.checkValidIntValue(year), Objects.requireNonNull(quarter));
}
/**
@@ -92,8 +92,9 @@ public final class YearQuarter implements Comparable, Serializable
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
- public static YearQuarter of(@Nonnull LocalDate date) {
- return of(date.getYear(), Quarter.fromMonth(date.getMonth()));
+ public static YearQuarter of(LocalDate date) {
+ AssertTools.checkNotNull(date);
+ return new YearQuarter(date.getYear(), Quarter.fromMonth(date.getMonth()));
}
/**
@@ -103,12 +104,13 @@ public final class YearQuarter implements Comparable, Serializable
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
- public static YearQuarter of(@Nonnull Date date) {
+ public static YearQuarter of(Date date) {
+ AssertTools.checkNotNull(date);
@SuppressWarnings("deprecation")
- final int year = date.getYear() + 1900;
+ final int yearValue = YEAR.checkValidIntValue(date.getYear() + 1900L);
@SuppressWarnings("deprecation")
- final int month = date.getMonth() + 1;
- return of(year, Quarter.fromMonth(month));
+ final int monthValue = date.getMonth() + 1;
+ return new YearQuarter(yearValue, Quarter.fromMonth(monthValue));
}
/**
@@ -119,7 +121,10 @@ public final class YearQuarter implements Comparable, Serializable
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(Calendar date) {
- return of(date.get(Calendar.YEAR), Quarter.fromMonth(date.get(Calendar.MONTH) + 1));
+ AssertTools.checkNotNull(date);
+ final int yearValue = ChronoField.YEAR.checkValidIntValue(date.get(Calendar.YEAR));
+ final int monthValue = date.get(Calendar.MONTH) + 1;
+ return new YearQuarter(yearValue, Quarter.fromMonth(monthValue));
}
/**
@@ -130,19 +135,29 @@ public final class YearQuarter implements Comparable, Serializable
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(YearMonth yearMonth) {
+ AssertTools.checkNotNull(yearMonth);
return of(yearMonth.getYear(), Quarter.fromMonth(yearMonth.getMonth()));
}
+ @StaticFactoryMethod(YearQuarter.class)
+ public static YearQuarter now() {
+ return of(LocalDate.now());
+ }
+
// #endregion
// #region - Getters
public int getYear() {
- return year;
+ return this.year;
}
public Quarter getQuarter() {
- return quarter;
+ return this.quarter;
+ }
+
+ public int getQuarterValue() {
+ return this.quarter.getValue();
}
public YearMonth firstYearMonth() {
@@ -181,33 +196,49 @@ public final class YearQuarter implements Comparable, Serializable
// #region - computes
- public YearQuarter plusQuarters(long quartersToAdd) { // TODO 单元测试
- if (quartersToAdd == 0) {
+ public YearQuarter plusQuarters(long quartersToAdd) {
+ if (quartersToAdd == 0L) {
return this;
}
long quarterCount = this.year * 4L + (this.quarter.getValue() - 1);
long calcQuarters = quarterCount + quartersToAdd; // safe overflow
int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcQuarters, 4));
int newQuarter = (int) Math.floorMod(calcQuarters, 4) + 1;
- return of(newYear, Quarter.of(newQuarter));
+ return new YearQuarter(newYear, Quarter.of(newQuarter));
}
- public YearQuarter minusQuarters(long quartersToAdd) { // TODO 单元测试
+ public YearQuarter minusQuarters(long quartersToAdd) {
return plusQuarters(-quartersToAdd);
}
- public YearQuarter plusYears(long yearsToAdd) { // TODO 单元测试
- if (yearsToAdd == 0) {
+ public YearQuarter nextQuarter() {
+ return plusQuarters(1L);
+ }
+
+ public YearQuarter lastQuarter() {
+ return minusQuarters(1L);
+ }
+
+ public YearQuarter plusYears(long yearsToAdd) {
+ if (yearsToAdd == 0L) {
return this;
}
int newYear = YEAR.checkValidIntValue(this.year + yearsToAdd); // safe overflow
- return of(newYear, this.quarter);
+ return new YearQuarter(newYear, this.quarter);
}
public YearQuarter minusYears(long yearsToAdd) {
return plusYears(-yearsToAdd);
}
+ public YearQuarter nextYear() {
+ return plusYears(1L);
+ }
+
+ public YearQuarter lastYear() {
+ return minusYears(1L);
+ }
+
// #endregion
// #region - hashCode & equals
@@ -231,7 +262,7 @@ public final class YearQuarter implements Comparable, Serializable
// #endregion
- // #region - compareTo
+ // #region - compare
@Override
public int compareTo(YearQuarter other) {
@@ -250,6 +281,14 @@ public final class YearQuarter implements Comparable, 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
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java
index 1fd381d..8ad67ee 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/ArrayTools.java
@@ -16,26 +16,45 @@
package xyz.zhouxy.plusone.commons.util;
+import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+/**
+ * ArrayTools
+ *
+ *
+ * 数组工具类
+ *
+ *
+ * @author ZhouXY
+ * @since 0.1.0
+ */
public class ArrayTools {
// #region - empty arrays
public static final char[] EMPTY_CHAR_ARRAY = {};
- public static final int[] EMPTY_INTEGER_ARRAY = {};
+ public static final byte[] EMPTY_BYTE_ARRAY = {};
+ public static final short[] EMPTY_SHORT_ARRAY = {};
+ public static final int[] EMPTY_INT_ARRAY = {};
public static final long[] EMPTY_LONG_ARRAY = {};
public static final float[] EMPTY_FLOAT_ARRAY = {};
public static final double[] EMPTY_DOUBLE_ARRAY = {};
// #endregion
+ public static final int NOT_FOUND_INDEX = -1;
+
// #region - isNullOrEmpty
// isNullOrEmpty
@@ -51,6 +70,61 @@ public class ArrayTools {
return arr == null || arr.length == 0;
}
+ // isNullOrEmpty - char
+ /**
+ * 检查给定数组是否为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNullOrEmpty(@Nullable char[] arr) {
+ return arr == null || arr.length == 0;
+ }
+
+ // isNullOrEmpty - byte
+ /**
+ * 检查给定数组是否为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNullOrEmpty(@Nullable byte[] arr) {
+ return arr == null || arr.length == 0;
+ }
+
+ // isNullOrEmpty - short
+ /**
+ * 检查给定数组是否为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNullOrEmpty(@Nullable short[] arr) {
+ return arr == null || arr.length == 0;
+ }
+
+ // isNullOrEmpty - int
+ /**
+ * 检查给定数组是否为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNullOrEmpty(@Nullable int[] arr) {
+ return arr == null || arr.length == 0;
+ }
+
+ // isNullOrEmpty - long
+ /**
+ * 检查给定数组是否为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNullOrEmpty(@Nullable long[] arr) {
+ return arr == null || arr.length == 0;
+ }
+
// isNullOrEmpty - float
/**
* 检查给定数组是否为空
@@ -73,39 +147,6 @@ public class ArrayTools {
return arr == null || arr.length == 0;
}
- // isNullOrEmpty - byte
- /**
- * 检查给定数组是否为空
- *
- * @param arr 待检查的数组,可以为 {@code null}
- * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
- */
- public static boolean isNullOrEmpty(@Nullable byte[] arr) {
- return arr == null || arr.length == 0;
- }
-
- // isNullOrEmpty - long
- /**
- * 检查给定数组是否为空
- *
- * @param arr 待检查的数组,可以为 {@code null}
- * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
- */
- public static boolean isNullOrEmpty(@Nullable long[] arr) {
- return arr == null || arr.length == 0;
- }
-
- // isNullOrEmpty - int
- /**
- * 检查给定数组是否为空
- *
- * @param arr 待检查的数组,可以为 {@code null}
- * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false}
- */
- public static boolean isNullOrEmpty(@Nullable int[] arr) {
- return arr == null || arr.length == 0;
- }
-
// #endregion
// #region - isNotEmpty
@@ -122,6 +163,61 @@ public class ArrayTools {
return arr != null && arr.length > 0;
}
+ // isNotEmpty - char
+ /**
+ * 检查给定数组是否不为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNotEmpty(@Nullable char[] arr) {
+ return arr != null && arr.length > 0;
+ }
+
+ // isNotEmpty - byte
+ /**
+ * 检查给定数组是否不为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNotEmpty(@Nullable byte[] arr) {
+ return arr != null && arr.length > 0;
+ }
+
+ // isNotEmpty - short
+ /**
+ * 检查给定数组是否不为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNotEmpty(@Nullable short[] arr) {
+ return arr != null && arr.length > 0;
+ }
+
+ // isNotEmpty - int
+ /**
+ * 检查给定数组是否不为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNotEmpty(@Nullable int[] arr) {
+ return arr != null && arr.length > 0;
+ }
+
+ // isNotEmpty - long
+ /**
+ * 检查给定数组是否不为空
+ *
+ * @param arr 待检查的数组,可以为 {@code null}
+ * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
+ */
+ public static boolean isNotEmpty(@Nullable long[] arr) {
+ return arr != null && arr.length > 0;
+ }
+
// isNotEmpty - float
/**
* 检查给定数组是否不为空
@@ -144,39 +240,6 @@ public class ArrayTools {
return arr != null && arr.length > 0;
}
- // isNotEmpty - byte
- /**
- * 检查给定数组是否不为空
- *
- * @param arr 待检查的数组,可以为 {@code null}
- * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
- */
- public static boolean isNotEmpty(@Nullable byte[] arr) {
- return arr != null && arr.length > 0;
- }
-
- // isNotEmpty - long
- /**
- * 检查给定数组是否不为空
- *
- * @param arr 待检查的数组,可以为 {@code null}
- * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
- */
- public static boolean isNotEmpty(@Nullable long[] arr) {
- return arr != null && arr.length > 0;
- }
-
- // isNotEmpty - int
- /**
- * 检查给定数组是否不为空
- *
- * @param arr 待检查的数组,可以为 {@code null}
- * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false}
- */
- public static boolean isNotEmpty(@Nullable int[] arr) {
- return arr != null && arr.length > 0;
- }
-
// #endregion
// #region - isAllElementsNotNull
@@ -198,12 +261,7 @@ public class ArrayTools {
*/
public static boolean isAllElementsNotNull(@Nonnull final T[] arr) {
AssertTools.checkArgument(arr != null, "The array cannot be null.");
- for (T element : arr) {
- if (element == null) {
- return false;
- }
- }
- return true;
+ return Arrays.stream(arr).allMatch(Objects::nonNull);
}
// #endregion
@@ -216,14 +274,18 @@ public class ArrayTools {
* @param arrays 数组集合,可以为 {@code null}
* @return 拼接后的数组
*/
- public static float[] concatFloatArray(@Nullable Collection arrays) {
+ public static char[] concatCharArray(@Nullable final Collection arrays) {
if (arrays == null || arrays.isEmpty()) {
- return new float[0];
+ return new char[0];
}
- final int length = arrays.stream().mapToInt(a -> a.length).sum();
- final float[] result = new float[length];
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
+ final char[] result = new char[length];
int i = 0;
- for (float[] arr : arrays) {
+ for (char[] arr : arraysToConcat) {
System.arraycopy(arr, 0, result, i, arr.length);
i += arr.length;
}
@@ -236,34 +298,18 @@ public class ArrayTools {
* @param arrays 数组集合,可以为 {@code null}
* @return 拼接后的数组
*/
- public static double[] concatDoubleArray(@Nullable Collection arrays) {
- if (arrays == null || arrays.isEmpty()) {
- return new double[0];
- }
- final int length = arrays.stream().mapToInt(a -> a.length).sum();
- final double[] result = new double[length];
- int i = 0;
- for (double[] arr : arrays) {
- System.arraycopy(arr, 0, result, i, arr.length);
- i += arr.length;
- }
- return result;
- }
-
- /**
- * 拼接多个数组
- *
- * @param arrays 数组集合,可以为 {@code null}
- * @return 拼接后的数组
- */
- public static byte[] concatByteArray(@Nullable Collection arrays) {
+ public static byte[] concatByteArray(@Nullable final Collection arrays) {
if (arrays == null || arrays.isEmpty()) {
return new byte[0];
}
- final int length = arrays.stream().mapToInt(a -> a.length).sum();
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
final byte[] result = new byte[length];
int i = 0;
- for (byte[] arr : arrays) {
+ for (byte[] arr : arraysToConcat) {
System.arraycopy(arr, 0, result, i, arr.length);
i += arr.length;
}
@@ -276,14 +322,18 @@ public class ArrayTools {
* @param arrays 数组集合,可以为 {@code null}
* @return 拼接后的数组
*/
- public static long[] concatLongArray(@Nullable Collection arrays) {
+ public static short[] concatShortArray(@Nullable final Collection arrays) {
if (arrays == null || arrays.isEmpty()) {
- return new long[0];
+ return new short[0];
}
- final int length = arrays.stream().mapToInt(a -> a.length).sum();
- final long[] result = new long[length];
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
+ final short[] result = new short[length];
int i = 0;
- for (long[] arr : arrays) {
+ for (short[] arr : arraysToConcat) {
System.arraycopy(arr, 0, result, i, arr.length);
i += arr.length;
}
@@ -296,14 +346,90 @@ public class ArrayTools {
* @param arrays 数组集合,可以为 {@code null}
* @return 拼接后的数组
*/
- public static int[] concatIntArray(@Nullable Collection arrays) {
+ public static int[] concatIntArray(@Nullable final Collection arrays) {
if (arrays == null || arrays.isEmpty()) {
return new int[0];
}
- final int length = arrays.stream().mapToInt(a -> a.length).sum();
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
final int[] result = new int[length];
int i = 0;
- for (int[] arr : arrays) {
+ for (int[] arr : arraysToConcat) {
+ System.arraycopy(arr, 0, result, i, arr.length);
+ i += arr.length;
+ }
+ return result;
+ }
+
+ /**
+ * 拼接多个数组
+ *
+ * @param arrays 数组集合,可以为 {@code null}
+ * @return 拼接后的数组
+ */
+ public static long[] concatLongArray(@Nullable final Collection arrays) {
+ if (arrays == null || arrays.isEmpty()) {
+ return new long[0];
+ }
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
+ final long[] result = new long[length];
+ int i = 0;
+ for (long[] arr : arraysToConcat) {
+ System.arraycopy(arr, 0, result, i, arr.length);
+ i += arr.length;
+ }
+ return result;
+ }
+
+ /**
+ * 拼接多个数组
+ *
+ * @param arrays 数组集合,可以为 {@code null}
+ * @return 拼接后的数组
+ */
+ public static float[] concatFloatArray(@Nullable final Collection arrays) {
+ if (arrays == null || arrays.isEmpty()) {
+ return new float[0];
+ }
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
+ final float[] result = new float[length];
+ int i = 0;
+ for (float[] arr : arraysToConcat) {
+ System.arraycopy(arr, 0, result, i, arr.length);
+ i += arr.length;
+ }
+ return result;
+ }
+
+ /**
+ * 拼接多个数组
+ *
+ * @param arrays 数组集合,可以为 {@code null}
+ * @return 拼接后的数组
+ */
+ public static double[] concatDoubleArray(@Nullable final Collection arrays) {
+ if (arrays == null || arrays.isEmpty()) {
+ return new double[0];
+ }
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
+ final double[] result = new double[length];
+ int i = 0;
+ for (double[] arr : arraysToConcat) {
System.arraycopy(arr, 0, result, i, arr.length);
i += arr.length;
}
@@ -317,17 +443,21 @@ public class ArrayTools {
* @param 泛型参数,表示数组的元素类型
* @return 返回连接后的列表,如果输入的集合为空或包含空数组,则返回空列表
*/
- public static List concatToList(@Nullable Collection arrays) {
+ public static List concatToList(@Nullable final Collection arrays) {
// 如果输入的集合是否为空,则直接返回一个空列表
if (arrays == null || arrays.isEmpty()) {
return Collections.emptyList();
}
+ final Collection arraysToConcat = arrays
+ .stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
// 计算所有数组的总长度,用于初始化列表的容量
- final int length = arrays.stream().mapToInt(a -> a.length).sum();
+ final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum();
final List result = new ArrayList<>(length);
- for (T[] arr : arrays) {
+ for (T[] arr : arraysToConcat) {
Collections.addAll(result, arr);
}
@@ -337,11 +467,162 @@ public class ArrayTools {
// #endregion
+ // #region - repeat
+
+ // repeat - char
+
+ public static char[] repeat(char[] arr, int times) {
+ return repeat(arr, times, Integer.MAX_VALUE);
+ }
+
+ public static char[] repeat(char[] arr, int times, int maxLength) {
+ AssertTools.checkArgument(Objects.nonNull(arr));
+ AssertTools.checkArgument(times >= 0,
+ "The number of times must be greater than or equal to zero");
+ AssertTools.checkArgument(maxLength >= 0,
+ "The max length must be greater than or equal to zero");
+ if (times == 0) {
+ return EMPTY_CHAR_ARRAY;
+ }
+ final int length = Integer.min(arr.length * times, maxLength);
+ final char[] result = new char[length];
+ fill(result, 0, length, arr);
+ return result;
+ }
+
+ // repeat - byte
+
+ public static byte[] repeat(byte[] arr, int times) {
+ return repeat(arr, times, Integer.MAX_VALUE);
+ }
+
+ public static byte[] repeat(byte[] arr, int times, int maxLength) {
+ AssertTools.checkArgument(Objects.nonNull(arr));
+ AssertTools.checkArgument(times >= 0,
+ "The number of times must be greater than or equal to zero");
+ AssertTools.checkArgument(maxLength >= 0,
+ "The max length must be greater than or equal to zero");
+ if (times == 0) {
+ return EMPTY_BYTE_ARRAY;
+ }
+ final int length = Integer.min(arr.length * times, maxLength);
+ final byte[] result = new byte[length];
+ fill(result, 0, length, arr);
+ return result;
+ }
+
+ // repeat - short
+
+ public static short[] repeat(short[] arr, int times) {
+ return repeat(arr, times, Integer.MAX_VALUE);
+ }
+
+ public static short[] repeat(short[] arr, int times, int maxLength) {
+ AssertTools.checkArgument(Objects.nonNull(arr));
+ AssertTools.checkArgument(times >= 0,
+ "The number of times must be greater than or equal to zero");
+ AssertTools.checkArgument(maxLength >= 0,
+ "The max length must be greater than or equal to zero");
+ if (times == 0) {
+ return EMPTY_SHORT_ARRAY;
+ }
+ final int length = Integer.min(arr.length * times, maxLength);
+ final short[] result = new short[length];
+ fill(result, 0, length, arr);
+ return result;
+ }
+
+ // repeat - int
+
+ public static int[] repeat(int[] arr, int times) {
+ return repeat(arr, times, Integer.MAX_VALUE);
+ }
+
+ public static int[] repeat(int[] arr, int times, int maxLength) {
+ AssertTools.checkArgument(Objects.nonNull(arr));
+ AssertTools.checkArgument(times >= 0,
+ "The number of times must be greater than or equal to zero");
+ AssertTools.checkArgument(maxLength >= 0,
+ "The max length must be greater than or equal to zero");
+ if (times == 0) {
+ return EMPTY_INT_ARRAY;
+ }
+ final int length = Integer.min(arr.length * times, maxLength);
+ final int[] result = new int[length];
+ fill(result, 0, length, arr);
+ return result;
+ }
+
+ // repeat - long
+
+ public static long[] repeat(long[] arr, int times) {
+ return repeat(arr, times, Integer.MAX_VALUE);
+ }
+
+ public static long[] repeat(long[] arr, int times, int maxLength) {
+ AssertTools.checkArgument(Objects.nonNull(arr));
+ AssertTools.checkArgument(times >= 0,
+ "The number of times must be greater than or equal to zero");
+ AssertTools.checkArgument(maxLength >= 0,
+ "The max length must be greater than or equal to zero");
+ if (times == 0) {
+ return EMPTY_LONG_ARRAY;
+ }
+ final int length = Integer.min(arr.length * times, maxLength);
+ final long[] result = new long[length];
+ fill(result, 0, length, arr);
+ return result;
+ }
+
+ // repeat - float
+
+ public static float[] repeat(float[] arr, int times) {
+ return repeat(arr, times, Integer.MAX_VALUE);
+ }
+
+ public static float[] repeat(float[] arr, int times, int maxLength) {
+ AssertTools.checkArgument(Objects.nonNull(arr));
+ AssertTools.checkArgument(times >= 0,
+ "The number of times must be greater than or equal to zero");
+ AssertTools.checkArgument(maxLength >= 0,
+ "The max length must be greater than or equal to zero");
+ if (times == 0) {
+ return EMPTY_FLOAT_ARRAY;
+ }
+ final int length = Integer.min(arr.length * times, maxLength);
+ final float[] result = new float[length];
+ fill(result, 0, length, arr);
+ return result;
+ }
+
+ // repeat - double
+
+ public static double[] repeat(double[] arr, int times) {
+ return repeat(arr, times, Integer.MAX_VALUE);
+ }
+
+ public static double[] repeat(double[] arr, int times, int maxLength) {
+ AssertTools.checkArgument(Objects.nonNull(arr));
+ AssertTools.checkArgument(times >= 0,
+ "The number of times must be greater than or equal to zero");
+ AssertTools.checkArgument(maxLength >= 0,
+ "The max length must be greater than or equal to zero");
+ if (times == 0) {
+ return EMPTY_DOUBLE_ARRAY;
+ }
+ final int length = Integer.min(arr.length * times, maxLength);
+ final double[] result = new double[length];
+ fill(result, 0, length, arr);
+ return result;
+ }
+
+ // #endregion
+
// #region - fill
// fill - char
- public static void fill(char[] a, char... values) {
+ public static void fill(char[] a, char[] values) {
fill(a, 0, a.length, values);
}
@@ -349,9 +630,96 @@ public class ArrayTools {
fill(a, 0, a.length, values != null ? values.toCharArray() : EMPTY_CHAR_ARRAY);
}
- public static void fill(char[] a, int fromIndex, int toIndex, char... values) {
- AssertTools.checkArgumentNotNull(a);
- if (values.length == 0) {
+ public static void fill(char[] a, int fromIndex, int toIndex, char[] values) {
+ AssertTools.checkArgument(Objects.nonNull(a));
+ if (values == null || values.length == 0) {
+ return;
+ }
+ final int start = Integer.max(fromIndex, 0);
+ final int end = Integer.min(toIndex, a.length);
+ if (start >= end) {
+ return;
+ }
+ for (int i = start; i < end; i += values.length) {
+ for (int j = 0; j < values.length; j++) {
+ final int k = (i + j);
+ if (k < end) {
+ a[k] = values[j];
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+
+ // fill - byte
+
+ public static void fill(byte[] a, byte[] values) {
+ fill(a, 0, a.length, values);
+ }
+
+ public static void fill(byte[] a, int fromIndex, int toIndex, byte[] values) {
+ AssertTools.checkArgument(Objects.nonNull(a));
+ if (values == null || values.length == 0) {
+ return;
+ }
+ final int start = Integer.max(fromIndex, 0);
+ final int end = Integer.min(toIndex, a.length);
+ if (start >= end) {
+ return;
+ }
+ for (int i = start; i < end; i += values.length) {
+ for (int j = 0; j < values.length; j++) {
+ final int k = (i + j);
+ if (k < end) {
+ a[k] = values[j];
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+
+ // fill - short
+
+ public static void fill(short[] a, short[] values) {
+ fill(a, 0, a.length, values);
+ }
+
+ public static void fill(short[] a, int fromIndex, int toIndex, short[] values) {
+ AssertTools.checkArgument(Objects.nonNull(a));
+ if (values == null || values.length == 0) {
+ return;
+ }
+ final int start = Integer.max(fromIndex, 0);
+ final int end = Integer.min(toIndex, a.length);
+ if (start >= end) {
+ return;
+ }
+ for (int i = start; i < end; i += values.length) {
+ for (int j = 0; j < values.length; j++) {
+ final int k = (i + j);
+ if (k < end) {
+ a[k] = values[j];
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+
+ // fill - int
+
+ public static void fill(int[] a, int[] values) {
+ fill(a, 0, a.length, values);
+ }
+
+ public static void fill(int[] a, int fromIndex, int toIndex, int[] values) {
+ AssertTools.checkArgument(Objects.nonNull(a));
+ if (values == null || values.length == 0) {
return;
}
final int start = Integer.max(fromIndex, 0);
@@ -374,13 +742,13 @@ public class ArrayTools {
// fill - long
- public static void fill(long[] a, long... values) {
+ public static void fill(long[] a, long[] values) {
fill(a, 0, a.length, values);
}
- public static void fill(long[] a, int fromIndex, int toIndex, long... values) {
- AssertTools.checkArgumentNotNull(a);
- if (values.length == 0) {
+ public static void fill(long[] a, int fromIndex, int toIndex, long[] values) {
+ AssertTools.checkArgument(Objects.nonNull(a));
+ if (values == null || values.length == 0) {
return;
}
final int start = Integer.max(fromIndex, 0);
@@ -403,13 +771,13 @@ public class ArrayTools {
// fill - float
- public static void fill(float[] a, float... values) {
+ public static void fill(float[] a, float[] values) {
fill(a, 0, a.length, values);
}
- public static void fill(float[] a, int fromIndex, int toIndex, float... values) {
- AssertTools.checkArgumentNotNull(a);
- if (values.length == 0) {
+ public static void fill(float[] a, int fromIndex, int toIndex, float[] values) {
+ AssertTools.checkArgument(Objects.nonNull(a));
+ if (values == null || values.length == 0) {
return;
}
final int start = Integer.max(fromIndex, 0);
@@ -432,13 +800,13 @@ public class ArrayTools {
// fill - double
- public static void fill(double[] a, double... values) {
+ public static void fill(double[] a, double[] values) {
fill(a, 0, a.length, values);
}
- public static void fill(double[] a, int fromIndex, int toIndex, double... values) {
- AssertTools.checkArgumentNotNull(a);
- if (values.length == 0) {
+ public static void fill(double[] a, int fromIndex, int toIndex, double[] values) {
+ AssertTools.checkArgument(Objects.nonNull(a));
+ if (values == null || values.length == 0) {
return;
}
final int start = Integer.max(fromIndex, 0);
@@ -470,7 +838,7 @@ public class ArrayTools {
}
private static void fillInternal(@Nonnull T[] a, int fromIndex, int toIndex, @Nullable T[] values) {
- AssertTools.checkArgumentNotNull(a);
+ AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
}
@@ -494,6 +862,256 @@ public class ArrayTools {
// #endregion
+ // #region - indexOf
+
+ public static int indexOfWithPredicate(T[] arr, Predicate super T> predicate) {
+ AssertTools.checkNotNull(predicate);
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (predicate.test(arr[i])) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int indexOf(T[] arr, T obj) {
+ return indexOfWithPredicate(arr, item -> Objects.equals(item, obj));
+ }
+
+ public static int indexOf(char[] arr, char value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == value) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int indexOf(byte[] arr, byte value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == value) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int indexOf(short[] arr, short value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == value) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int indexOf(int[] arr, int value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == value) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int indexOf(long[] arr, long value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == value) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int indexOf(float[] arr, float value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == value) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int indexOf(double[] arr, double value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == value) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ // #endregion
+
+ // #region - lastIndexOf
+
+ public static int lastIndexOfWithPredicate(T[] arr, @Nonnull Predicate super T> predicate) {
+ AssertTools.checkNotNull(predicate);
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (predicate.test(arr[i])) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int lastIndexOf(T[] arr, T obj) {
+ return lastIndexOfWithPredicate(arr, item -> Objects.equals(item, obj));
+ }
+
+ public static int lastIndexOf(char[] arr, char value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (value == arr[i]) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int lastIndexOf(byte[] arr, byte value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (value == arr[i]) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int lastIndexOf(short[] arr, short value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (value == arr[i]) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int lastIndexOf(int[] arr, int value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (value == arr[i]) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int lastIndexOf(long[] arr, long value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (value == arr[i]) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int lastIndexOf(float[] arr, float value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (value == arr[i]) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ public static int lastIndexOf(double[] arr, double value) {
+ if (isNullOrEmpty(arr)) {
+ return NOT_FOUND_INDEX;
+ }
+ for (int i = arr.length - 1; i >= 0; i--) {
+ if (value == arr[i]) {
+ return i;
+ }
+ }
+ return NOT_FOUND_INDEX;
+ }
+
+ // #endregion
+
+ // #region - contains
+
+ public static boolean contains(T[] arr, T obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean contains(char[] arr, char obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean contains(byte[] arr, byte obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean contains(short[] arr, short obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean contains(int[] arr, int obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean contains(long[] arr, long obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean contains(float[] arr, float obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean contains(double[] arr, double obj) {
+ return indexOf(arr, obj) > NOT_FOUND_INDEX;
+ }
+
+ public static boolean containsValue(BigDecimal[] arr, BigDecimal obj) {
+ return indexOfWithPredicate(arr, item -> BigDecimals.equalsValue(item, obj)) > NOT_FOUND_INDEX;
+ }
+
+ // #endregion
+
// #region - private constructor
private ArrayTools() {
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/AssertTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/AssertTools.java
index 8212eb7..cf31308 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/AssertTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/AssertTools.java
@@ -16,10 +16,15 @@
package xyz.zhouxy.plusone.commons.util;
+import java.util.Objects;
+import java.util.Optional;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
+import xyz.zhouxy.plusone.commons.exception.DataNotExistsException;
+import xyz.zhouxy.plusone.commons.exception.system.DataOperationResultException;
+
/**
* 断言工具
*
@@ -30,91 +35,249 @@ import javax.annotation.Nonnull;
*
* AssertTools.checkArgument(StringUtils.hasText(str), "The argument cannot be blank.");
* AssertTools.checkState(ArrayUtils.isNotEmpty(result), "The result cannot be empty.");
- * AssertTools.checkCondition(!CollectionUtils.isEmpty(roles), () -> new InvalidInputException("The roles cannot be empty."));
- * AssertTools.checkCondition(RegexTools.matches(email, PatternConsts.EMAIL), "must be a well-formed email address");
+ * AssertTools.checkCondition(!CollectionUtils.isEmpty(roles),
+ * () -> new InvalidInputException("The roles cannot be empty."));
+ * AssertTools.checkCondition(RegexTools.matches(email, PatternConsts.EMAIL),
+ * "must be a well-formed email address");
*
*
* @author ZhouXY
*/
public class AssertTools {
- // #region - checkArgument
-
- public static void checkArgumentNotNull(T argument) {
- checkCondition(argument != null, () -> new IllegalArgumentException("The argument cannot be null."));
- }
-
- public static void checkArgumentNotNull(T argument, String errMsg) {
- checkCondition(argument != null, () -> new IllegalArgumentException(errMsg));
- }
-
- public static void checkArgumentNotNull(T argument, Supplier messageSupplier) {
- checkCondition(argument != null, () -> new IllegalArgumentException(messageSupplier.get()));
- }
-
- public static void checkArgumentNotNull(T argument, String format, Object... args) {
- checkCondition(argument != null, () -> new IllegalArgumentException(String.format(format, args)));
- }
+ // ================================
+ // #region - Argument
+ // ================================
+ /** Throw {@link IllegalArgumentException} if the {@code condition} is false. */
public static void checkArgument(boolean condition) {
checkCondition(condition, IllegalArgumentException::new);
}
+ /** Throw {@link IllegalArgumentException} if the {@code condition} is false. */
public static void checkArgument(boolean condition, String errMsg) {
checkCondition(condition, () -> new IllegalArgumentException(errMsg));
}
- public static void checkArgument(boolean condition, Supplier messageSupplier) {
+ /** Throw {@link IllegalArgumentException} if the {@code condition} is false. */
+ public static void checkArgument(boolean condition, @Nonnull Supplier messageSupplier) {
checkCondition(condition, () -> new IllegalArgumentException(messageSupplier.get()));
}
+ /** Throw {@link IllegalArgumentException} if the {@code condition} is false. */
public static void checkArgument(boolean condition, String format, Object... args) {
checkCondition(condition, () -> new IllegalArgumentException(String.format(format, args)));
}
- // #endregion
+ // ================================
+ // #endregion - Argument
+ // ================================
- // #region - checkState
+ // ================================
+ // #region - State
+ // ================================
+ /** Throw {@link IllegalStateException} if the {@code condition} is false. */
public static void checkState(boolean condition) {
checkCondition(condition, IllegalStateException::new);
}
+ /** Throw {@link IllegalStateException} if the {@code condition} is false. */
public static void checkState(boolean condition, String errMsg) {
checkCondition(condition, () -> new IllegalStateException(errMsg));
}
- public static void checkState(boolean condition, Supplier messageSupplier) {
+ /** Throw {@link IllegalStateException} if the {@code condition} is false. */
+ public static void checkState(boolean condition, @Nonnull Supplier messageSupplier) {
checkCondition(condition, () -> new IllegalStateException(messageSupplier.get()));
}
+ /** Throw {@link IllegalStateException} if the {@code condition} is false. */
public static void checkState(boolean condition, String format, Object... args) {
checkCondition(condition, () -> new IllegalStateException(String.format(format, args)));
}
+ // ================================
// #endregion
+ // ================================
- // #region - checkNotNull
+ // ================================
+ // #region - NotNull
+ // ================================
+ /** Throw {@link NullPointerException} if the {@code obj} is null. */
public static void checkNotNull(T obj) {
checkCondition(obj != null, NullPointerException::new);
}
+ /** Throw {@link NullPointerException} if the {@code obj} is null. */
public static void checkNotNull(T obj, String errMsg) {
checkCondition(obj != null, () -> new NullPointerException(errMsg));
}
- public static void checkNotNull(T obj, Supplier messageSupplier) {
+ /** Throw {@link NullPointerException} if the {@code obj} is null. */
+ public static void checkNotNull(T obj, @Nonnull Supplier messageSupplier) {
checkCondition(obj != null, () -> new NullPointerException(messageSupplier.get()));
}
+ /** Throw {@link NullPointerException} if the {@code obj} is null. */
public static void checkNotNull(T obj, String format, Object... args) {
checkCondition(obj != null, () -> new NullPointerException(String.format(format, args)));
}
+ // ================================
// #endregion
+ // ================================
- // #region - checkCondition
+ // ================================
+ // #region - Exists
+ // ================================
+
+ /** Throw {@link DataNotExistsException} if the {@code obj} is null. */
+ public static T checkExists(T obj)
+ throws DataNotExistsException {
+ checkCondition(Objects.nonNull(obj), DataNotExistsException::new);
+ return obj;
+ }
+
+ /** Throw {@link DataNotExistsException} if the {@code obj} is null. */
+ public static T checkExists(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 checkExists(T obj, @Nonnull Supplier 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 checkExists(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 checkExists(@Nonnull Optional optional)
+ throws DataNotExistsException {
+ checkCondition(optional.isPresent(), DataNotExistsException::new);
+ return optional.get();
+ }
+
+ /** Throw {@link DataNotExistsException} if the {@code optional} is present. */
+ public static T checkExists(@Nonnull Optional 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 checkExists(@Nonnull Optional optional, @Nonnull Supplier 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 checkExists(@Nonnull Optional optional, String format, Object... args)
+ throws DataNotExistsException {
+ checkCondition(optional.isPresent(), () -> new DataNotExistsException(String.format(format, args)));
+ return optional.get();
+ }
+
+ // ================================
+ // #endregion - Exists
+ // ================================
+
+ // ================================
+ // #region - AffectedRows
+ // ================================
+
+ public static void checkAffectedRows(int expectedValue, int result) {
+ checkAffectedRows(expectedValue, result,
+ "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) {
+ checkCondition(expectedValue == result, () -> new DataOperationResultException(message));
+ }
+
+ public static void checkAffectedRows(int expectedValue, int result,
+ @Nonnull Supplier messageSupplier) {
+ checkCondition(expectedValue == result,
+ () -> new DataOperationResultException(messageSupplier.get()));
+ }
+
+ public static void checkAffectedRows(int expectedValue, int result, String format, Object... args) {
+ checkCondition(expectedValue == result,
+ () -> new DataOperationResultException(String.format(format, args)));
+ }
+
+ public static void checkAffectedRows(long expectedValue, long result) {
+ checkAffectedRows(expectedValue, result,
+ "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) {
+ checkCondition(expectedValue == result, () -> new DataOperationResultException(message));
+ }
+
+ public static void checkAffectedRows(long expectedValue, long result,
+ @Nonnull Supplier messageSupplier) {
+ checkCondition(expectedValue == result,
+ () -> new DataOperationResultException(messageSupplier.get()));
+ }
+
+ public static void checkAffectedRows(long expectedValue, long result, String format, Object... args) {
+ checkCondition(expectedValue == result,
+ () -> new DataOperationResultException(String.format(format, args)));
+ }
+
+ public static void checkAffectedOneRow(int result) {
+ checkAffectedRows(1, result,
+ () -> "The number of rows affected is expected to be 1, but is: " + result);
+ }
+
+ public static void checkAffectedOneRow(int result, String message) {
+ checkAffectedRows(1, result, message);
+ }
+
+ public static void checkAffectedOneRow(int result, @Nonnull Supplier messageSupplier) {
+ checkAffectedRows(1, result, messageSupplier);
+ }
+
+ public static void checkAffectedOneRow(int result, String format, Object... args) {
+ checkAffectedRows(1, result, format, args);
+ }
+
+ public static void checkAffectedOneRow(long result) {
+ checkAffectedRows(1L, result,
+ () -> "The number of rows affected is expected to be 1, but is: " + result);
+ }
+
+ public static void checkAffectedOneRow(long result, String message) {
+ checkAffectedRows(1L, result, message);
+ }
+
+ public static void checkAffectedOneRow(long result, @Nonnull Supplier messageSupplier) {
+ checkAffectedRows(1L, result, messageSupplier);
+ }
+
+ public static void checkAffectedOneRow(long result, String format, Object... args) {
+ checkAffectedRows(1L, result, format, args);
+ }
+
+ // ================================
+ // #endregion - AffectedRows
+ // ================================
+
+ // ================================
+ // #region - Condition
+ // ================================
public static void checkCondition(boolean condition, @Nonnull Supplier e)
throws T {
@@ -123,13 +286,19 @@ public class AssertTools {
}
}
+ // ================================
// #endregion
+ // ================================
- // #region - private constructor
+ // ================================
+ // #region - constructor
+ // ================================
private AssertTools() {
throw new IllegalStateException("Utility class");
}
+ // ================================
// #endregion
+ // ================================
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/BigDecimals.java b/src/main/java/xyz/zhouxy/plusone/commons/util/BigDecimals.java
index 90a707e..1b006c7 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/BigDecimals.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/BigDecimals.java
@@ -17,21 +17,31 @@
package xyz.zhouxy.plusone.commons.util;
import java.math.BigDecimal;
-import javax.annotation.Nullable;
-import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
+/**
+ * BigDecimals
+ *
+ *
+ * BigDecimal 工具类
+ *
+ *
+ * @author ZhouXY
+ * @since 0.1.0
+ */
public class BigDecimals {
public static boolean equalsValue(@Nullable BigDecimal a, @Nullable BigDecimal b) {
- return (a == b) || (a != null && a.compareTo(b) == 0);
+ return (a == b) || (a != null && b != null && a.compareTo(b) == 0);
}
public static boolean gt(BigDecimal a, BigDecimal b) {
- Preconditions.checkNotNull(a, "Parameter could not be null.");
- Preconditions.checkNotNull(b, "Parameter could not be null.");
+ AssertTools.checkNotNull(a, "Parameter could not be null.");
+ AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a != b) && (a.compareTo(b) > 0);
}
@@ -40,8 +50,8 @@ public class BigDecimals {
}
public static boolean lt(BigDecimal a, BigDecimal b) {
- Preconditions.checkNotNull(a, "Parameter could not be null.");
- Preconditions.checkNotNull(b, "Parameter could not be null.");
+ AssertTools.checkNotNull(a, "Parameter could not be null.");
+ AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a != b) && (a.compareTo(b) < 0);
}
@@ -49,6 +59,25 @@ public class BigDecimals {
return lt(a, b) || equalsValue(a, b);
}
+ public static BigDecimal sum(final BigDecimal... numbers) {
+ if (ArrayTools.isNullOrEmpty(numbers)) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal result = BigDecimals.nullToZero(numbers[0]);
+ for (int i = 1; i < numbers.length; i++) {
+ BigDecimal value = numbers[i];
+ if (value != null) {
+ result = result.add(value);
+ }
+ }
+ return result;
+ }
+
+ @Nonnull
+ public static BigDecimal nullToZero(@Nullable final BigDecimal val) {
+ return val != null ? val : BigDecimal.ZERO;
+ }
+
@StaticFactoryMethod(BigDecimal.class)
public static BigDecimal of(final String val) {
return (StringTools.isNotBlank(val)) ? new BigDecimal(val) : BigDecimal.ZERO;
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/ConcurrentHashMapTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/ConcurrentHashMapTools.java
deleted file mode 100644
index c36ecf0..0000000
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/ConcurrentHashMapTools.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2023-2024 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.util;
-
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-
-import xyz.zhouxy.plusone.commons.base.JRE;
-import xyz.zhouxy.plusone.commons.collection.SafeConcurrentHashMap;
-
-/**
- * ConcurrentHashMapTools
- *
- *
- * Java 8 的 {@link ConcurrentHashMap#computeIfAbsent(Object, Function)} 方法有 bug,
- * 可使用这个工具类的 {@link computeIfAbsentForJava8} 进行替换。
- *
- *
- * NOTE: 方法来自Dubbo,见:issues#2349
- *
- * @author ZhouXY
- * @since 1.0
- * @see ConcurrentHashMap
- * @see SafeConcurrentHashMap
- */
-public class ConcurrentHashMapTools {
-
- public static V computeIfAbsent(
- ConcurrentHashMap map, final K key, // NOSONAR
- final Function super K, ? extends V> mappingFunction) {
- Objects.requireNonNull(map, "map");
- return JRE.isJava8()
- ? computeIfAbsentForJava8(map, key, mappingFunction)
- : map.computeIfAbsent(key, mappingFunction);
- }
-
- public static V computeIfAbsentForJava8(
- ConcurrentHashMap map, final K key, // NOSONAR
- final Function super K, ? extends V> mappingFunction) {
- Objects.requireNonNull(key);
- Objects.requireNonNull(mappingFunction);
- V v = map.get(key);
- if (null == v) {
- v = mappingFunction.apply(key);
- if (null == v) {
- return null;
- }
- final V res = map.putIfAbsent(key, v);
- if (null != res) {
- return res;
- }
- }
- return v;
- }
-
- private ConcurrentHashMapTools() {
- throw new IllegalStateException("Utility class");
- }
-}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java
index c2597fe..e580706 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/DateTimeTools.java
@@ -16,24 +16,23 @@
package xyz.zhouxy.plusone.commons.util;
+import static java.time.temporal.ChronoField.*;
+
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
+import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
+import java.time.chrono.IsoChronology;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
-import javax.annotation.Nonnull;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Range;
import xyz.zhouxy.plusone.commons.time.Quarter;
import xyz.zhouxy.plusone.commons.time.YearQuarter;
@@ -45,86 +44,35 @@ import xyz.zhouxy.plusone.commons.time.YearQuarter;
*/
public class DateTimeTools {
- /**
- * 缓存时间格式化器
- */
- private static final LoadingCache DATE_TIME_FORMATTER_CACHE = CacheBuilder.newBuilder()
- .maximumSize(20)
- .build(new CacheLoader() {
- @Override
- public DateTimeFormatter load(@Nonnull String pattern) throws Exception {
- return DateTimeFormatter.ofPattern(pattern);
- }
- });
+ // #region - toString
- /**
- * 获取时间格式化器
- *
- * @param pattern 时间格式
- * @return 时间格式化器
- */
- public static DateTimeFormatter getDateTimeFormatter(String pattern) {
- return DATE_TIME_FORMATTER_CACHE.getUnchecked(pattern);
+ public static String toYearString(int year) {
+ return Integer.toString(YEAR.checkValidIntValue(year));
}
- /**
- * 将日期时间转换为指定格式的字符串
- *
- * @param pattern 时间格式
- * @param dateTime 日期时间
- * @return 格式化的字符串
- */
- public static String toString(String pattern, ZonedDateTime dateTime) {
- return getDateTimeFormatter(pattern).format(dateTime);
+ public static String toYearString(Year year) {
+ return year.toString();
}
- /**
- * 将时间戳转换为指定格式的字符串,使用系统默认时区
- *
- * @param pattern 时间格式
- * @param instant 时间戳
- * @return 格式化的字符串
- */
- public static String toString(String pattern, Instant instant) {
- ZonedDateTime dateTime = instant.atZone(ZoneId.systemDefault());
- return toString(pattern, dateTime);
+ public static String toMonthStringM(int monthValue) {
+ return Integer.toString(MONTH_OF_YEAR.checkValidIntValue(monthValue));
}
- /**
- * 将时间戳转换为指定格式的字符串,使用指定时区
- *
- * @param pattern 时间格式
- * @param instant 时间戳
- * @param zone 时区
- * @return 格式化的字符串
- */
- public static String toString(String pattern, Instant instant, ZoneId zone) {
- ZonedDateTime dateTime = instant.atZone(zone);
- return toString(pattern, dateTime);
+ public static String toMonthStringMM(int monthValue) {
+ return String.format("%02d", MONTH_OF_YEAR.checkValidIntValue(monthValue));
}
- /**
- * 指定格式,返回当前时间戳对应的字符串
- *
- * @param pattern 时间格式
- * @return 格式化的字符串
- */
- public static String nowStr(String pattern) {
- return toString(pattern, ZonedDateTime.now());
+ public static String toMonthStringM(Month month) {
+ return Integer.toString(month.getValue());
}
- /**
- * 指定格式,返回当前时间戳对应的字符串,使用指定时区
- *
- * @param pattern 时间格式
- * @param zone 时区
- * @return 格式化的字符串
- */
- public static String nowStr(String pattern, ZoneId zone) {
- return toString(pattern, Instant.now().atZone(zone));
+ public static String toMonthStringMM(Month month) {
+ return String.format("%02d", month.getValue());
}
- // toDate
+ // #endregion
+
+ // #region - toDate
/**
* 将时间戳转换为 {@link Date} 对象
@@ -189,7 +137,9 @@ public class DateTimeTools {
return Date.from(ZonedDateTime.of(localDate, localTime, zone).toInstant());
}
- // toInstant
+ // #endregion
+
+ // #region - toInstant
/**
* 将时间戳转换为 {@link Instant} 对象
@@ -226,9 +176,7 @@ public class DateTimeTools {
*
* @param zonedDateTime {@link ZonedDateTime} 对象
* @return {@link Instant} 对象
- * @deprecated 请使用 {@link ZonedDateTime#toInstant()} 方法
*/
- @Deprecated
public static Instant toInstant(ZonedDateTime zonedDateTime) { // NOSONAR
return zonedDateTime.toInstant();
}
@@ -244,7 +192,9 @@ public class DateTimeTools {
return ZonedDateTime.of(localDateTime, zone).toInstant();
}
- // toZonedDateTime
+ // #endregion
+
+ // #region - toZonedDateTime
/**
* 获取时间戳在指定时区的地区时间。
@@ -330,15 +280,14 @@ public class DateTimeTools {
* @param localDateTime 地区时间
* @param zone 时区
* @return 带时区的地区时间
- *
- * @deprecated 使用 {@link ZonedDateTime#of(LocalDateTime, ZoneId)}
*/
- @Deprecated
- public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, ZoneId zone) { // NOSONAR
+ public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, ZoneId zone) {
return ZonedDateTime.of(localDateTime, zone);
}
- // toLocalDateTime
+ // #endregion
+
+ // #region - toLocalDateTime
/**
* 获取时间戳在指定时区的地区时间。
@@ -406,9 +355,11 @@ public class DateTimeTools {
return LocalDateTime.ofInstant(zonedDateTime.toInstant(), zone);
}
+ // #endregion
+
// ====================
- // toJodaInstant
+ // #region - toJodaInstant
/**
* 将 {@link java.time.Instant} 转换为 {@link org.joda.time.Instant}
@@ -441,7 +392,9 @@ public class DateTimeTools {
return toJodaInstant(java.time.ZonedDateTime.of(localDateTime, zone));
}
- // toJavaInstant
+ // #endregion
+
+ // #region - toJavaInstant
/**
* 将 {@link org.joda.time.Instant} 对象转换为 {@link java.time.Instant} 对象
@@ -479,7 +432,9 @@ public class DateTimeTools {
return toJavaInstant(localDateTime.toDateTime(zone));
}
- // toJodaDateTime
+ // #endregion
+
+ // #region - toJodaDateTime
/**
* 将 Java 中表示日期时间的 {@link java.time.ZonedDateTime} 对象
@@ -506,7 +461,7 @@ public class DateTimeTools {
public static org.joda.time.DateTime toJodaDateTime(
java.time.LocalDateTime localDateTime,
java.time.ZoneId zone) {
- org.joda.time.DateTimeZone dateTimeZone = toJodaTime(zone);
+ org.joda.time.DateTimeZone dateTimeZone = toJodaZone(zone);
return toJodaInstant(ZonedDateTime.of(localDateTime, zone).toInstant()).toDateTime(dateTimeZone);
}
@@ -520,11 +475,13 @@ public class DateTimeTools {
public static org.joda.time.DateTime toJodaDateTime(
java.time.Instant instant,
java.time.ZoneId zone) {
- org.joda.time.DateTimeZone dateTimeZone = toJodaTime(zone);
+ org.joda.time.DateTimeZone dateTimeZone = toJodaZone(zone);
return toJodaInstant(instant).toDateTime(dateTimeZone);
}
- // toZonedDateTime
+ // #endregion
+
+ // #region - toZonedDateTime
/**
* 将 joda-time 中带时区的日期时间,转换为 java.time 中带时区的日期时间
@@ -568,7 +525,9 @@ public class DateTimeTools {
return toJavaInstant(instant).atZone(zone);
}
- // toJodaLocalDateTime
+ // #endregion
+
+ // #region - toJodaLocalDateTime
/**
* 将 {@link java.time.LocalDateTime} 转换为 {@link org.joda.time.LocalDateTime}
@@ -578,11 +537,13 @@ public class DateTimeTools {
*/
public static org.joda.time.LocalDateTime toJodaLocalDateTime(java.time.LocalDateTime localDateTime) {
java.time.ZoneId javaZone = java.time.ZoneId.systemDefault();
- org.joda.time.DateTimeZone jodaZone = toJodaTime(javaZone);
+ org.joda.time.DateTimeZone jodaZone = toJodaZone(javaZone);
return toJodaInstant(localDateTime, javaZone).toDateTime(jodaZone).toLocalDateTime();
}
- // toJavaLocalDateTime
+ // #endregion
+
+ // #region - toJavaLocalDateTime
/**
* 将 {@link org.joda.time.LocalDateTime} 转换为 {@link java.time.LocalDateTime}
@@ -596,6 +557,10 @@ public class DateTimeTools {
return toJavaInstant(localDateTime, jodaZone).atZone(javaZone).toLocalDateTime();
}
+ // #endregion
+
+ // #region - ZoneId <--> DateTimeZone
+
/**
* 转换 Java API 和 joda-time API 表示时区的对象
*
@@ -612,11 +577,13 @@ public class DateTimeTools {
* @param zone Java API 中表示时区的对象
* @return joda-time API 中表示时区的对象
*/
- public static org.joda.time.DateTimeZone toJodaTime(java.time.ZoneId zone) {
+ public static org.joda.time.DateTimeZone toJodaZone(java.time.ZoneId zone) {
return org.joda.time.DateTimeZone.forID(zone.getId());
}
- // getQuarter
+ // #endregion
+
+ // #region - YearQuarter & Quarter
/**
* 获取指定日期所在季度
@@ -679,6 +646,44 @@ public class DateTimeTools {
return YearQuarter.of(date);
}
+ // #endregion
+
+ // ================================
+ // #region - others
+ // ================================
+
+ public static LocalDate startDateOfYear(int year) {
+ return LocalDate.ofYearDay(year, 1);
+ }
+
+ public static LocalDate endDateOfYear(int year) {
+ return LocalDate.of(year, 12, 31);
+ }
+
+ public static LocalDateTime startOfNextDate(LocalDate date) {
+ return date.plusDays(1L).atStartOfDay();
+ }
+
+ public static ZonedDateTime startOfNextDate(LocalDate date, ZoneId zone) {
+ return date.plusDays(1L).atStartOfDay(zone);
+ }
+
+ public static Range toDateTimeRange(LocalDate date) {
+ return Range.closedOpen(date.atStartOfDay(), startOfNextDate(date));
+ }
+
+ public static Range toDateTimeRange(LocalDate date, ZoneId zone) {
+ return Range.closedOpen(date.atStartOfDay(zone), startOfNextDate(date, zone));
+ }
+
+ public static boolean isLeapYear(int year) {
+ return IsoChronology.INSTANCE.isLeapYear(year);
+ }
+
+ // ================================
+ // #endregion - others
+ // ================================
+
/**
* 私有构造方法,明确标识该常量类的作用。
*/
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/EnumTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/EnumTools.java
index 428b29c..ba1f76a 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/EnumTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/EnumTools.java
@@ -18,10 +18,9 @@ package xyz.zhouxy.plusone.commons.util;
import java.util.function.Supplier;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import com.google.common.base.Preconditions;
-
/**
* 枚举工具类
*
@@ -36,44 +35,57 @@ public final class EnumTools {
/**
* 通过 ordinal 获取枚举实例
*
- * @param 枚举的类型
- * @param clazz 枚举的类型信息
- * @param ordinal 序号
+ * @param 枚举的类型
+ * @param enumType 枚举的类型信息
+ * @param ordinal 序号
* @return 枚举对象
* @deprecated 不推荐使用枚举的 ordinal。
*/
@Deprecated
- public static > E valueOf(Class clazz, int ordinal) { // NOSONAR 该方法弃用,但不删掉
- Preconditions.checkNotNull(clazz, "Clazz must not be null.");
- E[] values = clazz.getEnumConstants();
+ private static > E valueOfInternal(@Nonnull Class enumType, int ordinal) { // NOSONAR 该方法弃用,但不删掉
+ E[] values = enumType.getEnumConstants();
AssertTools.checkCondition((ordinal >= 0 && ordinal < values.length),
- () -> new EnumConstantNotPresentException(clazz, Integer.toString(ordinal)));
+ () -> new EnumConstantNotPresentException(enumType, Integer.toString(ordinal)));
return values[ordinal];
}
/**
* 通过 ordinal 获取枚举实例
*
- * @param 枚举的类型
- * @param clazz 枚举的类型信息
- * @param ordinal 序号
- * @param defaultValue 默认值
+ * @param 枚举的类型
+ * @param enumType 枚举的类型信息
+ * @param ordinal 序号
* @return 枚举对象
* @deprecated 不推荐使用枚举的 ordinal。
*/
@Deprecated
- public static > E valueOf(Class clazz, @Nullable Integer ordinal, E defaultValue) { // NOSONAR 该方法弃用,但不删掉
- if (null == ordinal) {
- return defaultValue;
- }
- return valueOf(clazz, ordinal);
+ public static > E valueOf(Class enumType, int ordinal) { // NOSONAR 该方法弃用,但不删掉
+ AssertTools.checkNotNull(enumType, "Enum type must not be null.");
+ return valueOfInternal(enumType, ordinal);
}
/**
* 通过 ordinal 获取枚举实例
*
* @param 枚举的类型
- * @param clazz 枚举的类型信息
+ * @param enumType 枚举的类型信息
+ * @param ordinal 序号
+ * @param defaultValue 默认值
+ * @return 枚举对象
+ * @deprecated 不推荐使用枚举的 ordinal。
+ */
+ @Deprecated
+ public static > E valueOf(Class enumType, // NOSONAR 该方法弃用,但不删掉
+ @Nullable Integer ordinal, E defaultValue) {
+ AssertTools.checkNotNull(enumType);
+ return null == ordinal ? defaultValue : valueOfInternal(enumType, ordinal);
+ }
+
+ /**
+ * 通过 ordinal 获取枚举实例
+ *
+ * @param 枚举的类型
+ * @param enumType 枚举的类型信息
* @param ordinal 序号
* @param defaultValue 默认值
* @return 枚举对象
@@ -81,29 +93,28 @@ public final class EnumTools {
*/
@Deprecated
public static > E getValueOrDefault( // NOSONAR 该方法弃用,但不删掉
- Class clazz,
+ Class enumType,
@Nullable Integer ordinal,
Supplier defaultValue) {
- if (null == ordinal) {
- return defaultValue.get();
- }
- return valueOf(clazz, ordinal);
+ AssertTools.checkNotNull(enumType);
+ AssertTools.checkNotNull(defaultValue);
+ return null == ordinal ? defaultValue.get() : valueOfInternal(enumType, ordinal);
}
/**
* 通过 ordinal 获取枚举实例
*
- * @param 枚举的类型
- * @param clazz 枚举的类型信息
- * @param ordinal 序号
+ * @param 枚举的类型
+ * @param enumType 枚举的类型信息
+ * @param ordinal 序号
* @return 枚举对象
* @deprecated 不推荐使用枚举的 ordinal。
*/
@Deprecated
- public static > E getValueOrDefault(Class clazz, @Nullable Integer ordinal) { // NOSONAR 该方法弃用,但不删掉
- return getValueOrDefault(clazz, ordinal, () -> {
- Preconditions.checkNotNull(clazz, "Clazz must not be null.");
- E[] values = clazz.getEnumConstants();
+ public static > E getValueOrDefault(Class enumType, @Nullable Integer ordinal) { // NOSONAR 该方法弃用,但不删掉
+ return getValueOrDefault(enumType, ordinal, () -> {
+ AssertTools.checkNotNull(enumType, "Enum type must not be null.");
+ E[] values = enumType.getEnumConstants();
return values[0];
});
}
@@ -111,69 +122,89 @@ public final class EnumTools {
/**
* 通过 ordinal 获取枚举实例
*
- * @param 枚举的类型
- * @param clazz 枚举的类型信息
- * @param ordinal 序号
+ * @param 枚举的类型
+ * @param enumType 枚举的类型信息
+ * @param ordinal 序号
* @return 枚举对象
* @deprecated 不推荐使用枚举的 ordinal。
*/
@Deprecated
- public static > E getValueNullable(Class clazz, @Nullable Integer ordinal) { // NOSONAR 该方法弃用,但不删掉
- return valueOf(clazz, ordinal, null);
+ public static > E getValueNullable(Class enumType, @Nullable Integer ordinal) { // NOSONAR 该方法弃用,但不删掉
+ return valueOf(enumType, ordinal, null);
}
- public static > Integer checkOrdinal(Class clazz, Integer ordinal) {
- Preconditions.checkNotNull(clazz, "Clazz must not be null.");
- Preconditions.checkNotNull(ordinal, "Ordinal must not be null.");
- E[] values = clazz.getEnumConstants();
- if (ordinal >= 0 && ordinal < values.length) {
- return ordinal;
- }
- throw new EnumConstantNotPresentException(clazz, Integer.toString(ordinal));
+ public static > Integer checkOrdinal(Class enumType, Integer ordinal) {
+ AssertTools.checkNotNull(enumType, "Enum type must not be null.");
+ AssertTools.checkNotNull(ordinal, "Ordinal must not be null.");
+ E[] values = enumType.getEnumConstants();
+ AssertTools.checkCondition(ordinal >= 0 && ordinal < values.length,
+ () -> new EnumConstantNotPresentException(enumType, Integer.toString(ordinal)));
+ return ordinal;
}
/**
* 校验枚举的 ordinal。
*
- * @param 枚举类型
- * @param clazz 枚举类型
- * @param ordinal The ordinal
+ * @param 枚举类型
+ * @param enumType 枚举类型
+ * @param ordinal The ordinal
* @return The ordinal
*/
@Nullable
- public static > Integer checkOrdinalNullable(Class clazz, @Nullable Integer ordinal) {
- return checkOrdinalOrDefault(clazz, ordinal, null);
+ public static > Integer checkOrdinalNullable(Class enumType, @Nullable Integer ordinal) {
+ return checkOrdinalOrDefault(enumType, ordinal, (Integer) null);
}
/**
* 校验枚举的 ordinal,如果 ordinal 为 {@code null},则返回 {@code 0}。
*
- * @param 枚举类型
- * @param clazz 枚举类型
- * @param ordinal The ordinal
+ * @param 枚举类型
+ * @param enumType 枚举类型
+ * @param ordinal The ordinal
* @return The ordinal
*/
@Nullable
- public static > Integer checkOrdinalOrDefault(Class clazz, @Nullable Integer ordinal) {
- return checkOrdinalOrDefault(clazz, ordinal, 0);
+ public static > Integer checkOrdinalOrDefault(Class enumType, @Nullable Integer ordinal) {
+ return checkOrdinalOrDefault(enumType, ordinal, 0);
}
/**
* 校验枚举的 ordinal,如果 ordinal 为 {@code null},则返回 {@code defaultValue}。
*
- * @param 枚举类型
- * @param clazz 枚举类型
- * @param ordinal The ordinal
+ * @param 枚举类型
+ * @param enumType 枚举类型
+ * @param ordinal The ordinal
* @return The ordinal
*/
@Nullable
public static > Integer checkOrdinalOrDefault(
- Class clazz,
+ Class enumType,
@Nullable Integer ordinal,
@Nullable Integer defaultValue) {
- if (ordinal != null) {
- return checkOrdinal(clazz, ordinal);
- }
- return defaultValue;
+ AssertTools.checkNotNull(enumType);
+ return checkOrdinalOrGetInternal(enumType, ordinal, () -> checkOrdinalOrDefaultInternal(enumType, defaultValue, null));
+ }
+
+ /**
+ * 仅对 {@code ordinal} 进行判断,不对 {@code defaultValue} 进行判断
+ */
+ @Nullable
+ private static > Integer checkOrdinalOrDefaultInternal(
+ @Nonnull Class enumType,
+ @Nullable Integer ordinal,
+ @Nullable Integer defaultValue) {
+ return ordinal != null
+ ? checkOrdinal(enumType, ordinal)
+ : defaultValue;
+ }
+
+ @Nullable
+ private static > Integer checkOrdinalOrGetInternal(
+ @Nonnull Class enumType,
+ @Nullable Integer ordinal,
+ @Nonnull Supplier defaultValueSupplier) {
+ return ordinal != null
+ ? checkOrdinal(enumType, ordinal)
+ : defaultValueSupplier.get();
}
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/Enumeration.java b/src/main/java/xyz/zhouxy/plusone/commons/util/Enumeration.java
index a6bfa8f..aee9e0b 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/Enumeration.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/Enumeration.java
@@ -24,8 +24,6 @@ import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
-import com.google.common.base.Preconditions;
-
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
/**
@@ -43,7 +41,7 @@ public abstract class Enumeration> // NOSONAR 暂不移
protected final String name;
protected Enumeration(final int id, final String name) {
- Preconditions.checkArgument(StringTools.isNotBlank(name), "Name of enumeration must has text.");
+ AssertTools.checkArgument(StringTools.isNotBlank(name), "Name of enumeration must has text.");
this.id = id;
this.name = name;
}
@@ -98,7 +96,7 @@ public abstract class Enumeration> // NOSONAR 暂不移
}
public T get(int id) {
- Preconditions.checkArgument(this.valueMap.containsKey(id), "[%s] 对应的值不存在", id);
+ AssertTools.checkArgument(this.valueMap.containsKey(id), "[%s] 对应的值不存在", id);
return this.valueMap.get(id);
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java b/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java
index 3301641..2bf8276 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/IdGenerator.java
@@ -17,12 +17,21 @@
package xyz.zhouxy.plusone.commons.util;
import java.util.Map;
+import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
-import com.google.common.annotations.Beta;
-
-@Beta
+/**
+ * ID 生成器
+ *
+ *
+ * 生成 UUID 和 修改版雪花ID(Seata 版本)
+ *
+ *
+ * @see UUID
+ * @see IdWorker
+ * @author ZhouXY
+ */
public class IdGenerator {
// ===== UUID =====
@@ -40,6 +49,7 @@ public class IdGenerator {
}
public static String toSimpleString(UUID uuid) {
+ AssertTools.checkArgument(Objects.nonNull(uuid));
return (uuidDigits(uuid.getMostSignificantBits() >> 32, 8) +
uuidDigits(uuid.getMostSignificantBits() >> 16, 4) +
uuidDigits(uuid.getMostSignificantBits(), 4) +
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java b/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java
index e806cb2..20b59bc 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/IdWorker.java
@@ -24,7 +24,26 @@ import java.util.concurrent.atomic.AtomicLong;
import xyz.zhouxy.plusone.commons.exception.system.NoAvailableMacFoundException;
-
+/**
+ * Seata 提供的修改版雪花ID。
+ *
+ * 大体思路为:
+ *
+ * 每个机器线程安全地生成序列,前面加上机器的id,这样就不会与其它机器的id相冲突。
+ * 时间戳作为序列的“预留位”,它更像是应用启动时最开始的序列的一部分,在一个时间戳里生成 4096 个 id 之后,直接生成下一个时间戳的 id。
+ *
+ *
+ *
+ * 详情见以下介绍:
+ *
+ *
+ * @author ZhouXY
+ */
public class IdWorker {
/**
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java b/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java
index f79e72c..d8138f3 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java
@@ -17,12 +17,10 @@
package xyz.zhouxy.plusone.commons.util;
import java.math.BigDecimal;
+import java.math.BigInteger;
import javax.annotation.Nonnull;
-
-import com.google.common.base.Preconditions;
-
-import xyz.zhouxy.plusone.commons.math.IntervalType;
+import javax.annotation.Nullable;
/**
* Numbers
@@ -31,7 +29,7 @@ import xyz.zhouxy.plusone.commons.math.IntervalType;
*/
public class Numbers {
- // sum
+ // #region - sum
public static int sum(final short... numbers) {
int result = 0;
@@ -73,105 +71,64 @@ public class Numbers {
return result;
}
- public static BigDecimal sum(final BigDecimal... numbers) {
- BigDecimal result = BigDecimals.of("0.00");
- for (BigDecimal number : numbers) {
- result = result.add(number);
+ public static BigInteger sum(final BigInteger... numbers) {
+ if (ArrayTools.isNullOrEmpty(numbers)) {
+ return BigInteger.ZERO;
+ }
+ BigInteger result = Numbers.nullToZero(numbers[0]);
+ for (int i = 1; i < numbers.length; i++) {
+ BigInteger value = numbers[i];
+ if (value != null) {
+ result = result.add(value);
+ }
}
return result;
}
- // between
-
- public static boolean between(int value, int min, int max) {
- return between(value, min, max, IntervalType.CLOSED_OPEN);
+ public static BigDecimal sum(final BigDecimal... numbers) {
+ return BigDecimals.sum(numbers);
}
- public static boolean between(int value, int min, int max, IntervalType intervalType) {
- final IntervalType intervalTypeToUse = intervalType != null
- ? intervalType
- : IntervalType.CLOSED_OPEN;
- switch (intervalTypeToUse) {
- case OPEN:
- return min < value && value < max;
- case CLOSED:
- return min <= value && value <= max;
- case OPEN_CLOSED:
- return min < value && value <= max;
- case CLOSED_OPEN:
- default:
- return min <= value && value < max;
- }
+ // #endregion
+
+ // #region - nullToZero
+
+ public static byte nullToZero(@Nullable final Byte val) {
+ return val != null ? val : 0;
}
- public static boolean between(long value, long min, long max) {
- return between(value, min, max, IntervalType.CLOSED_OPEN);
+ public static short nullToZero(@Nullable final Short val) {
+ return val != null ? val : 0;
}
- public static boolean between(long value, long min, long max, IntervalType intervalType) {
- final IntervalType intervalTypeToUse = intervalType != null
- ? intervalType
- : IntervalType.CLOSED_OPEN;
- switch (intervalTypeToUse) {
- case OPEN:
- return min < value && value < max;
- case CLOSED:
- return min <= value && value <= max;
- case OPEN_CLOSED:
- return min < value && value <= max;
- case CLOSED_OPEN:
- default:
- return min <= value && value < max;
- }
+ public static int nullToZero(@Nullable final Integer val) {
+ return val != null ? val : 0;
}
- public static boolean between(double value, double min, double max) {
- return between(value, min, max, IntervalType.CLOSED_OPEN);
+ public static long nullToZero(@Nullable final Long val) {
+ return val != null ? val : 0L;
}
- public static boolean between(double value, double min, double max, IntervalType intervalType) {
- final IntervalType intervalTypeToUse = intervalType != null
- ? intervalType
- : IntervalType.CLOSED_OPEN;
- switch (intervalTypeToUse) {
- case OPEN:
- return min < value && value < max;
- case CLOSED:
- return min <= value && value <= max;
- case OPEN_CLOSED:
- return min < value && value <= max;
- case CLOSED_OPEN:
- default:
- return min <= value && value < max;
- }
+ public static float nullToZero(@Nullable final Float val) {
+ return val != null ? val : 0.0F;
}
- public static > boolean between(@Nonnull T value, T min, T max) {
- return between(value, min, max, IntervalType.CLOSED_OPEN);
+ public static double nullToZero(@Nullable final Double val) {
+ return val != null ? val : 0.0;
}
- public static > boolean between(@Nonnull T value, T min, T max, IntervalType intervalType) {
- Preconditions.checkArgument(value != null, "The value to valid connot be null.");
- IntervalType intervalTypeToUse = intervalType != null
- ? intervalType
- : IntervalType.CLOSED_OPEN;
- switch (intervalTypeToUse) {
- case OPEN:
- return (min == null || min.compareTo(value) < 0)
- && (max == null || value.compareTo(max) < 0);
- case CLOSED:
- return (min == null || min.compareTo(value) <= 0)
- && (max == null || value.compareTo(max) <= 0);
- case OPEN_CLOSED:
- return (min == null || min.compareTo(value) < 0)
- && (max == null || value.compareTo(max) <= 0);
- case CLOSED_OPEN:
- default:
- return (min == null || min.compareTo(value) <= 0)
- && (max == null || value.compareTo(max) < 0);
- }
+ @Nonnull
+ public static BigInteger nullToZero(@Nullable final BigInteger val) {
+ return val != null ? val : BigInteger.ZERO;
}
+ @Nonnull
+ public static BigDecimal nullToZero(@Nullable final BigDecimal val) {
+ return BigDecimals.nullToZero(val);
+ }
+
+ // #endregion
+
private Numbers() {
throw new IllegalStateException("Utility class");
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java
index 6afc578..07f54bf 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/RandomTools.java
@@ -16,20 +16,48 @@
package xyz.zhouxy.plusone.commons.util;
+import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nonnull;
+/**
+ * 随机工具类
+ *
+ * 建议调用方自行维护 Random 对象
+ *
+ * @author ZhouXY
+ */
public final class RandomTools {
- public static final SecureRandom DEFAULT_SECURE_RANDOM = new SecureRandom();
+ private static final SecureRandom DEFAULT_SECURE_RANDOM;
+
+ static {
+ SecureRandom secureRandom = null;
+ try {
+ secureRandom = SecureRandom.getInstanceStrong(); // 获取高强度安全随机数生成器
+ }
+ catch (NoSuchAlgorithmException e) {
+ secureRandom = new SecureRandom(); // 获取普通的安全随机数生成器
+ }
+ DEFAULT_SECURE_RANDOM = secureRandom;
+ }
public static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz";
public static final String NUMBERS = "0123456789";
+ public static SecureRandom defaultSecureRandom() {
+ return DEFAULT_SECURE_RANDOM;
+ }
+
+ public static ThreadLocalRandom currentThreadLocalRandom() {
+ return ThreadLocalRandom.current();
+ }
+
/**
* 使用传入的随机数生成器,生成指定长度的字符串
*
@@ -41,20 +69,20 @@ public final class RandomTools {
* @return 随机字符串
*/
public static String randomStr(@Nonnull Random random, @Nonnull char[] sourceCharacters, int length) {
- AssertTools.checkArgumentNotNull(random, "Random cannot be null.");
- AssertTools.checkArgumentNotNull(sourceCharacters, "Source characters cannot be null.");
+ 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) {
- AssertTools.checkArgumentNotNull(sourceCharacters, "Source characters 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(ThreadLocalRandom.current(), sourceCharacters, length);
}
public static String secureRandomStr(@Nonnull char[] sourceCharacters, int length) {
- AssertTools.checkArgumentNotNull(sourceCharacters, "Source characters 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(DEFAULT_SECURE_RANDOM, sourceCharacters, length);
}
@@ -70,20 +98,20 @@ public final class RandomTools {
* @return 随机字符串
*/
public static String randomStr(@Nonnull Random random, @Nonnull String sourceCharacters, int length) {
- AssertTools.checkArgumentNotNull(random, "Random cannot be null.");
- AssertTools.checkArgumentNotNull(sourceCharacters, "Source characters cannot be null.");
+ 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) {
- AssertTools.checkArgumentNotNull(sourceCharacters, "Source characters 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(ThreadLocalRandom.current(), sourceCharacters, length);
}
public static String secureRandomStr(@Nonnull String sourceCharacters, int length) {
- AssertTools.checkArgumentNotNull(sourceCharacters, "Source characters 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(DEFAULT_SECURE_RANDOM, sourceCharacters, length);
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java
index 141a044..53551f8 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/RegexTools.java
@@ -26,8 +26,6 @@ import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import com.google.common.base.Preconditions;
-
/**
* 封装一些常用的正则操作,并可以缓存 {@link Pattern} 实例以复用(最多缓存大概 256 个)。
*
@@ -49,7 +47,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例
*/
public static Pattern getPattern(final String pattern, final boolean cachePattern) {
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(pattern);
return cachePattern ? cacheAndGetPatternInternal(pattern) : getPatternInternal(pattern);
}
@@ -60,7 +58,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例
*/
public static Pattern getPattern(final String pattern) {
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(pattern);
return getPatternInternal(pattern);
}
@@ -72,8 +70,8 @@ public final class RegexTools {
* @return {@link Pattern} 实例数组
*/
public static Pattern[] getPatterns(final String[] patterns, final boolean cachePattern) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
return cachePattern
? cacheAndGetPatternsInternal(patterns)
: getPatternsInternal(patterns);
@@ -86,27 +84,11 @@ public final class RegexTools {
* @return {@link Pattern} 实例数组
*/
public static Pattern[] getPatterns(final String[] patterns) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
return getPatternsInternal(patterns);
}
- /**
- * 手动缓存 Pattern 实例。
- *
- * @param pattern 要缓存的 {@link Pattern} 实例
- * @return 缓存的 Pattern 实例。如果缓存已满,则返回 {@code null}。
- */
- public static Pattern cachePattern(final Pattern pattern) {
- Preconditions.checkNotNull(pattern, "The pattern can not be null.");
- if (PATTERN_CACHE.size() >= MAX_CACHE_SIZE) {
- return null;
- }
- final String patternStr = pattern.pattern();
- final Pattern pre = PATTERN_CACHE.putIfAbsent(patternStr, pattern);
- return pre != null ? pre : pattern;
- }
-
/**
* 判断 {@code input} 是否匹配 {@code pattern}。
*
@@ -115,7 +97,7 @@ public final class RegexTools {
* @return 判断结果
*/
public static boolean matches(@Nullable final CharSequence input, final Pattern pattern) {
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(pattern);
return matchesInternal(input, pattern);
}
@@ -127,8 +109,8 @@ public final class RegexTools {
* @return 判断结果
*/
public static boolean matchesOne(@Nullable final CharSequence input, final Pattern[] patterns) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
return matchesOneInternal(input, patterns);
}
@@ -140,8 +122,8 @@ public final class RegexTools {
* @return 判断结果
*/
public static boolean matchesAll(@Nullable final CharSequence input, final Pattern[] patterns) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
return matchesAllInternal(input, patterns);
}
@@ -155,7 +137,7 @@ public final class RegexTools {
*/
public static boolean matches(@Nullable final CharSequence input, final String pattern,
final boolean cachePattern) {
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(pattern);
Pattern p = cachePattern
? cacheAndGetPatternInternal(pattern)
: getPatternInternal(pattern);
@@ -170,7 +152,7 @@ public final class RegexTools {
* @return 判断结果
*/
public static boolean matches(@Nullable final CharSequence input, final String pattern) {
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(pattern);
return matchesInternal(input, getPatternInternal(pattern));
}
@@ -184,8 +166,8 @@ public final class RegexTools {
*/
public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns,
final boolean cachePattern) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = cachePattern
? cacheAndGetPatternsInternal(patterns)
: getPatternsInternal(patterns);
@@ -200,8 +182,8 @@ public final class RegexTools {
* @return 判断结果
*/
public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = getPatternsInternal(patterns);
return matchesOneInternal(input, patternSet);
}
@@ -216,8 +198,8 @@ public final class RegexTools {
*/
public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns,
final boolean cachePattern) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = cachePattern
? cacheAndGetPatternsInternal(patterns)
: getPatternsInternal(patterns);
@@ -232,8 +214,8 @@ public final class RegexTools {
* @return 判断结果
*/
public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns) {
- Preconditions.checkNotNull(patterns);
- Preconditions.checkArgument(allNotNull(patterns));
+ AssertTools.checkNotNull(patterns);
+ AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = getPatternsInternal(patterns);
return matchesAllInternal(input, patternSet);
}
@@ -246,8 +228,8 @@ public final class RegexTools {
* @return 结果
*/
public static Matcher getMatcher(final CharSequence input, final Pattern pattern) {
- Preconditions.checkNotNull(input);
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(input);
+ AssertTools.checkNotNull(pattern);
return pattern.matcher(input);
}
@@ -260,8 +242,8 @@ public final class RegexTools {
* @return 结果
*/
public static Matcher getMatcher(final CharSequence input, final String pattern, boolean cachePattern) {
- Preconditions.checkNotNull(input);
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(input);
+ AssertTools.checkNotNull(pattern);
final Pattern p = cachePattern
? cacheAndGetPatternInternal(pattern)
: getPatternInternal(pattern);
@@ -276,8 +258,8 @@ public final class RegexTools {
* @return 结果
*/
public static Matcher getMatcher(final CharSequence input, final String pattern) {
- Preconditions.checkNotNull(input);
- Preconditions.checkNotNull(pattern);
+ AssertTools.checkNotNull(input);
+ AssertTools.checkNotNull(pattern);
return getPatternInternal(pattern).matcher(input);
}
@@ -287,7 +269,6 @@ public final class RegexTools {
* 获取 {@link Pattern} 实例。
*
* @param pattern 正则表达式
- * @param cachePattern 是否缓存 {@link Pattern} 实例
* @return {@link Pattern} 实例
*/
@Nonnull
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java
index 383a6dd..c8af751 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java
@@ -18,13 +18,9 @@ package xyz.zhouxy.plusone.commons.util;
import java.util.concurrent.TimeUnit;
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-
/**
- * Twitter_Snowflake
+ * Twitter 版雪花算法
*/
-@Beta
public class SnowflakeIdGenerator {
// ==============================Fields===========================================
@@ -68,9 +64,6 @@ public class SnowflakeIdGenerator {
/** 上次生成 ID 的时间截 */
private long lastTimestamp = -1L;
- /** 锁对象 */
- private final Object lock = new Object();
-
// ==============================Constructors=====================================
/**
@@ -80,9 +73,9 @@ public class SnowflakeIdGenerator {
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdGenerator(final long workerId, final long datacenterId) {
- Preconditions.checkArgument((workerId <= MAX_WORKER_ID && workerId >= 0),
+ AssertTools.checkArgument((workerId <= MAX_WORKER_ID && workerId >= 0),
"WorkerId can't be greater than %s or less than 0.", MAX_WORKER_ID);
- Preconditions.checkArgument((datacenterId <= MAX_DATACENTER_ID && datacenterId >= 0),
+ AssertTools.checkArgument((datacenterId <= MAX_DATACENTER_ID && datacenterId >= 0),
"DatacenterId can't be greater than %s or less than 0.", MAX_DATACENTER_ID);
this.datacenterIdAndWorkerId
= (datacenterId << DATACENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT);
@@ -94,51 +87,47 @@ public class SnowflakeIdGenerator {
*
* @return SnowflakeId
*/
- public long nextId() {
- long timestamp;
- synchronized (lock) {
- timestamp = timeGen();
+ public synchronized long nextId() {
+ long timestamp = timeGen();
- // 发生了回拨,此刻时间小于上次发号时间
- if (timestamp < lastTimestamp) {
- long offset = lastTimestamp - timestamp;
- if (offset <= 5) {
- // 时间偏差大小小于5ms,则等待两倍时间
- try {
- TimeUnit.MILLISECONDS.sleep(offset << 1);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new IllegalStateException(e);
- }
- timestamp = timeGen();
- if (timestamp < lastTimestamp) {
- // 还是小于,抛异常上报
- throwClockBackwardsEx(lastTimestamp, timestamp);
- }
- } else {
+ // 发生了回拨,此刻时间小于上次发号时间
+ if (timestamp < lastTimestamp) {
+ long offset = lastTimestamp - timestamp;
+ if (offset <= 5) {
+ // 时间偏差大小小于5ms,则等待两倍时间
+ try {
+ TimeUnit.MILLISECONDS.sleep(offset << 1);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(e);
+ }
+ timestamp = timeGen();
+ if (timestamp < lastTimestamp) {
+ // 还是小于,抛异常上报
throwClockBackwardsEx(lastTimestamp, timestamp);
}
+ } else {
+ throwClockBackwardsEx(lastTimestamp, timestamp);
}
-
- // 如果是同一时间生成的,则进行毫秒内序列
- if (lastTimestamp == timestamp) {
- sequence = (sequence + 1) & SEQUENCE_MASK;
- // 毫秒内序列溢出
- if (sequence == 0) {
- // 阻塞到下一个毫秒,获得新的时间戳
- timestamp = tilNextMillis(lastTimestamp);
- }
- }
- // 时间戳改变,毫秒内序列重置
- else {
- sequence = 0L;
- }
-
- // 上次生成 ID 的时间截
- lastTimestamp = timestamp;
-
}
+ // 如果是同一时间生成的,则进行毫秒内序列
+ if (lastTimestamp == timestamp) {
+ sequence = (sequence + 1) & SEQUENCE_MASK;
+ // 毫秒内序列溢出
+ if (sequence == 0) {
+ // 阻塞到下一个毫秒,获得新的时间戳
+ timestamp = tilNextMillis(lastTimestamp);
+ }
+ }
+ // 时间戳改变,毫秒内序列重置
+ else {
+ sequence = 0L;
+ }
+
+ // 上次生成 ID 的时间截
+ lastTimestamp = timestamp;
+
// 移位并通过或运算拼到一起组成64位的ID
return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | datacenterIdAndWorkerId | sequence;
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java b/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java
index 951ead0..5dd5659 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/StringTools.java
@@ -16,6 +16,8 @@
package xyz.zhouxy.plusone.commons.util;
+import java.util.Objects;
+
import com.google.common.annotations.Beta;
@Beta
@@ -35,6 +37,15 @@ public class StringTools {
return false;
}
+ 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) {
+ AssertTools.checkArgument(Objects.nonNull(str));
+ return String.valueOf(ArrayTools.repeat(str.toCharArray(), times, maxLength));
+ }
+
private StringTools() {
throw new IllegalStateException("Utility class");
}
diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java b/src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java
index 83b8af9..10e8e7a 100644
--- a/src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java
+++ b/src/main/java/xyz/zhouxy/plusone/commons/util/TreeBuilder.java
@@ -27,8 +27,6 @@ import java.util.stream.Collectors;
import javax.annotation.Nullable;
-import com.google.common.base.Preconditions;
-
/**
* TreeBuilder
*
@@ -63,7 +61,7 @@ public class TreeBuilder {
* @param nodes 平铺的节点列表
*/
public List buildTree(Collection nodes) {
- Preconditions.checkNotNull(nodes);
+ AssertTools.checkNotNull(nodes);
return buildTreeInternal(nodes, this.defaultComparator);
}
@@ -80,7 +78,7 @@ public class TreeBuilder {
* 仅影响调用 addChild 的顺序,如果操作对象本身对应的控制了子节点的顺序,无法影响其相关逻辑。
*/
public List buildTree(Collection nodes, @Nullable Comparator super T> comparator) {
- Preconditions.checkNotNull(nodes);
+ AssertTools.checkNotNull(nodes);
final Comparator super T> c = (comparator != null) ? comparator : this.defaultComparator;
return buildTreeInternal(nodes, c);
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/base/IWithCodeTests.java b/src/test/java/xyz/zhouxy/plusone/commons/base/IWithCodeTests.java
new file mode 100644
index 0000000..92c7da8
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/base/IWithCodeTests.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ * 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.base;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import javax.annotation.Nonnull;
+
+import org.junit.jupiter.api.Test;
+
+import xyz.zhouxy.plusone.commons.util.AssertTools;
+
+class IWithCodeTests {
+
+ @Test
+ void equalsCode_SameCode_ReturnsTrue() {
+ assertTrue(WithCode.INSTANCE.isCodeEquals("testCode"));
+ Integer intCode = 0;
+ Long longCode = 0L;
+ assertTrue(WithIntCode.INSTANCE.isCodeEquals(intCode));
+ assertTrue(WithLongCode.INSTANCE.isCodeEquals(intCode));
+ assertTrue(WithLongCode.INSTANCE.isCodeEquals(longCode));
+
+ assertTrue(WithCode.INSTANCE.isSameCodeAs(WithCode.SAME_CODE_INSTANCE));
+ assertTrue(WithIntCode.INSTANCE.isSameCodeAs(WithIntCode.SAME_CODE_INSTANCE));
+ assertTrue(WithIntCode.INSTANCE.isSameCodeAs(WithLongCode.SAME_CODE_INSTANCE));
+ assertTrue(WithLongCode.INSTANCE.isSameCodeAs(WithLongCode.SAME_CODE_INSTANCE));
+ assertTrue(WithLongCode.INSTANCE.isSameCodeAs(WithIntCode.SAME_CODE_INSTANCE));
+ }
+
+ @Test
+ void equalsCode_DifferentCode_ReturnsFalse() {
+ assertFalse(WithCode.INSTANCE.isCodeEquals("wrongCode"));
+ Integer intCode = 108;
+ Long longCode = 108L;
+ assertFalse(WithIntCode.INSTANCE.isCodeEquals(intCode));
+ assertFalse(WithLongCode.INSTANCE.isCodeEquals(intCode));
+ assertFalse(WithLongCode.INSTANCE.isCodeEquals(longCode));
+
+ assertFalse(WithCode.INSTANCE.isSameCodeAs(WithCode.WRONG_CODE_INSTANCE));
+ assertFalse(WithIntCode.INSTANCE.isSameCodeAs(WithIntCode.WRONG_CODE_INSTANCE));
+ assertFalse(WithIntCode.INSTANCE.isSameCodeAs(WithLongCode.WRONG_CODE_INSTANCE));
+ assertFalse(WithLongCode.INSTANCE.isSameCodeAs(WithLongCode.WRONG_CODE_INSTANCE));
+ assertFalse(WithLongCode.INSTANCE.isSameCodeAs(WithIntCode.WRONG_CODE_INSTANCE));
+ }
+
+ @Test
+ @SuppressWarnings("null")
+ void equalsCode_NullCode_ReturnsFalse() {
+ assertFalse(WithCode.INSTANCE.isSameCodeAs((WithCode) null));
+ assertFalse(WithCode.INSTANCE.isSameCodeAs((WithIntCode) null));
+ assertFalse(WithCode.INSTANCE.isSameCodeAs((WithLongCode) null));
+
+ assertFalse(WithIntCode.INSTANCE.isSameCodeAs((WithCode) null));
+ assertFalse(WithIntCode.INSTANCE.isSameCodeAs((WithIntCode) null));
+ assertFalse(WithIntCode.INSTANCE.isSameCodeAs((WithLongCode) null));
+
+ assertFalse(WithLongCode.INSTANCE.isSameCodeAs((WithCode) null));
+ assertFalse(WithLongCode.INSTANCE.isSameCodeAs((WithIntCode) null));
+ assertFalse(WithLongCode.INSTANCE.isSameCodeAs((WithLongCode) null));
+
+ assertFalse(WithCode.INSTANCE.isCodeEquals((String) null));
+ Integer intCode = null;
+ Long longCode = null;
+ assertThrows(NullPointerException.class, () -> WithIntCode.INSTANCE.isCodeEquals(intCode));
+ assertThrows(NullPointerException.class, () -> WithLongCode.INSTANCE.isCodeEquals(intCode));
+ assertThrows(NullPointerException.class, () -> WithLongCode.INSTANCE.isCodeEquals(longCode));
+ }
+
+ private static enum WithCode implements IWithCode {
+ INSTANCE("testCode"),
+ SAME_CODE_INSTANCE("testCode"),
+ WRONG_CODE_INSTANCE("wrongCode"),
+ ;
+
+ @Nonnull
+ private final String code;
+
+ WithCode(String code) {
+ AssertTools.checkNotNull(code);
+ this.code = code;
+ }
+
+ @Override
+ @Nonnull
+ public String getCode() {
+ return code;
+ }
+ }
+
+ private static enum WithIntCode implements IWithIntCode {
+ INSTANCE(0),
+ SAME_CODE_INSTANCE(0),
+ WRONG_CODE_INSTANCE(1),
+ ;
+
+ private final int code;
+
+ WithIntCode(int code) {
+ this.code = code;
+ }
+
+ @Override
+ public int getCode() {
+ return code;
+ }
+ }
+
+ private static enum WithLongCode implements IWithLongCode {
+ INSTANCE(0L),
+ SAME_CODE_INSTANCE(0L),
+ WRONG_CODE_INSTANCE(108L),
+ ;
+
+ private final long code;
+
+ WithLongCode(long code) {
+ this.code = code;
+ }
+
+ @Override
+ public long getCode() {
+ return code;
+ }
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/base/RefTests.java b/src/test/java/xyz/zhouxy/plusone/commons/base/RefTests.java
new file mode 100644
index 0000000..5ff8e22
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/base/RefTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 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.base;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+class RefTests {
+
+ @Test
+ void testRef() {
+ Ref strRef = Ref.of("ZhouXY");
+ assertTrue(strRef.checkValue("ZhouXY"::equals));
+ assertFalse(strRef.checkValue("ZhouXY1"::equals));
+ apply(strRef);
+ assertEquals("Hello ZhouXY", strRef.getValue());
+ assertTrue(strRef.checkValue("Hello ZhouXY"::equals));
+ log.info("strRef: {}", strRef);
+
+ Ref intStringRef = Ref.of("108");
+ Ref integerRef = intStringRef.transform(Integer::parseInt);
+ assertEquals(108, integerRef.getValue());
+ }
+
+ void apply(Ref strRef) {
+ strRef.transformValue(str -> "Hello " + str);
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java
new file mode 100644
index 0000000..97fdef5
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/collection/CollectionToolsTests.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 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.collection;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+public class CollectionToolsTests {
+ @Test
+ void testIsEmpty() {
+
+ List list = new ArrayList<>();
+ assertTrue(CollectionTools.isEmpty(list));
+ assertFalse(CollectionTools.isNotEmpty(list));
+
+ list.add("Test");
+ assertFalse(CollectionTools.isEmpty(list));
+ assertTrue(CollectionTools.isNotEmpty(list));
+
+ Map map = new HashMap<>();
+ assertTrue(CollectionTools.isEmpty(map));
+ assertFalse(CollectionTools.isNotEmpty(map));
+
+ map.put("2", 2);
+ assertFalse(CollectionTools.isEmpty(map));
+ assertTrue(CollectionTools.isNotEmpty(map));
+ }
+
+ @Test
+ void testNullToEmpty() {
+ List list = Lists.newArrayList("Java", "C", "C++", "C#");
+ assertSame(list, CollectionTools.nullToEmptyList(list));
+ assertEquals(Collections.emptyList(), CollectionTools.nullToEmptyList(null));
+
+ Set set = Sets.newHashSet("Java", "C", "C++", "C#");
+ assertSame(set, CollectionTools.nullToEmptySet(set));
+ assertEquals(Collections.emptySet(), CollectionTools.nullToEmptySet(null));
+
+ Map map = ImmutableMap.of("K1", 1, "K2", 2, "K3", 3);
+ assertSame(map, CollectionTools.nullToEmptyMap(map));
+ assertEquals(Collections.emptyMap(), CollectionTools.nullToEmptyMap(null));
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/constant/PatternConstsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/constant/PatternConstsTests.java
new file mode 100644
index 0000000..88304d6
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/constant/PatternConstsTests.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2024 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.constant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.regex.Matcher;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public //
+class PatternConstsTests {
+
+ // ================================
+ // #region - BASIC_ISO_DATE
+ // ================================
+
+ @Test
+ void testBasicIsoDate_ValidDate() {
+ Matcher matcher = PatternConsts.BASIC_ISO_DATE.matcher("20241229");
+ assertTrue(matcher.matches());
+
+ assertEquals("2024", matcher.group(1));
+ assertEquals("12", matcher.group(2));
+ assertEquals("29", matcher.group(3));
+ assertEquals("2024", matcher.group("yyyy"));
+ assertEquals("12", matcher.group("MM"));
+ assertEquals("29", matcher.group("dd"));
+
+ // LeapYearFeb29()
+ assertTrue(PatternConsts.BASIC_ISO_DATE.matcher("20200229").matches());
+
+ // BoundaryMin()
+ assertTrue(PatternConsts.BASIC_ISO_DATE.matcher("00000101").matches());
+
+ // BoundaryMax()
+ assertTrue(PatternConsts.BASIC_ISO_DATE.matcher("9999999991231").matches());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "20231301", // InvalidMonth
+ "20230230", // InvalidDay
+ "20210229", // NonLeapYearFeb29
+ })
+ void testBasicIsoDate_InvalidDate_butMatches(String date) {
+ // 虽然日期有误,但这个正则无法判断。实际工作中,应使用日期时间 API。
+ Matcher matcher = PatternConsts.BASIC_ISO_DATE.matcher(date);
+ assertTrue(matcher.matches());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "2023041", // TooShort
+ "99999999990415", // TooLong
+ "2023-04-15", // NonNumeric
+ })
+ void testBasicIsoDate_InvalidDate_Mismatches(String date) {
+ Matcher matcher = PatternConsts.BASIC_ISO_DATE.matcher(date);
+ assertFalse(matcher.matches());
+ }
+
+ // ================================
+ // #endregion - BASIC_ISO_DATE
+ // ================================
+
+ // ================================
+ // #region - ISO_LOCAL_DATE
+ // ================================
+
+ @Test
+ void testIsoLocalDate_ValidDate() {
+ Matcher matcher = PatternConsts.ISO_LOCAL_DATE.matcher("2024-12-29");
+ assertTrue(matcher.matches());
+ assertEquals("2024", matcher.group("yyyy"));
+ assertEquals("12", matcher.group("MM"));
+ assertEquals("29", matcher.group("dd"));
+
+ // LeapYearFeb29()
+ assertTrue(PatternConsts.ISO_LOCAL_DATE.matcher("2020-02-29").matches());
+
+ // BoundaryMin()
+ assertTrue(PatternConsts.ISO_LOCAL_DATE.matcher("0000-01-01").matches());
+
+ // BoundaryMax()
+ assertTrue(PatternConsts.ISO_LOCAL_DATE.matcher("999999999-12-31").matches());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "2023-13-01", // InvalidMonth
+ "2023-02-30", // InvalidDay
+ "2021-02-29", // NonLeapYearFeb29
+ })
+ void testIsoLocalDate_InvalidDate_butMatches(String date) {
+ // 虽然日期有误,但这个正则无法判断。实际工作中,应使用日期时间 API。
+ Matcher matcher = PatternConsts.ISO_LOCAL_DATE.matcher(date);
+ assertTrue(matcher.matches());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "2023-04-1", // TooShort
+ "9999999999-04-15", // TooLong
+ "20230415",
+ })
+ void testIsoLocalDate_InvalidDate_Mismatches(String date) {
+ Matcher matcher = PatternConsts.ISO_LOCAL_DATE.matcher(date);
+ assertFalse(matcher.matches());
+ }
+
+ // ================================
+ // #endregion - ISO_LOCAL_DATE
+ // ================================
+
+ // ================================
+ // #region - PASSWORD
+ // ================================
+
+ @Test
+ void testPassword_ValidPassword_Matches() {
+ assertTrue(PatternConsts.PASSWORD.matcher("Abc123!@#").matches());
+ }
+
+ @Test
+ void testPassword_InvalidPassword_Mismatches() {
+ assertFalse(PatternConsts.PASSWORD.matcher("Abc123 !@#").matches()); // 带空格
+ assertFalse(PatternConsts.PASSWORD.matcher("Abc123!@# ").matches()); // 带空格
+ assertFalse(PatternConsts.PASSWORD.matcher(" Abc123!@#").matches()); // 带空格
+ assertFalse(PatternConsts.PASSWORD.matcher(" Abc123!@# ").matches()); // 带空格
+ assertFalse(PatternConsts.PASSWORD.matcher("77553366998844113322").matches()); // 纯数字
+ assertFalse(PatternConsts.PASSWORD.matcher("poiujhgbfdsazxcfvghj").matches()); // 纯小写字母
+ assertFalse(PatternConsts.PASSWORD.matcher("POIUJHGBFDSAZXCFVGHJ").matches()); // 纯大写字母
+ assertFalse(PatternConsts.PASSWORD.matcher("!#$%&'*\\+-/=?^`{|}~@()[]\",.;':").matches()); // 纯特殊字符
+ assertFalse(PatternConsts.PASSWORD.matcher("sdfrghbv525842582752").matches()); // 没有小写字母
+ assertFalse(PatternConsts.PASSWORD.matcher("SDFRGHBV525842582752").matches()); // 没有小写字母
+ assertFalse(PatternConsts.PASSWORD.matcher("sdfrghbvSDFRGHBV").matches()); // 没有数字
+ assertFalse(PatternConsts.PASSWORD.matcher("Abc1!").matches()); // 太短
+ assertFalse(PatternConsts.PASSWORD.matcher("Abc1!Abc1!Abc1!Abc1!Abc1!Abc1!Abc1!").matches()); // 太长
+ assertFalse(PatternConsts.PASSWORD.matcher("").matches());
+ assertFalse(PatternConsts.PASSWORD.matcher(" ").matches());
+ }
+
+ // ================================
+ // #endregion - PASSWORD
+ // ================================
+
+ // ================================
+ // #region - EMAIL
+ // ================================
+
+ @Test
+ public void testValidEmails() {
+ assertTrue(PatternConsts.EMAIL.matcher("test@example.com").matches());
+ assertTrue(PatternConsts.EMAIL.matcher("user.name+tag+sorting@example.com").matches());
+ assertTrue(PatternConsts.EMAIL.matcher("user@sub.example.com").matches());
+ assertTrue(PatternConsts.EMAIL.matcher("user@123.123.123.123").matches());
+ }
+
+ @Test
+ public void testInvalidEmails() {
+ assertFalse(PatternConsts.EMAIL.matcher(".username@example.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("@missingusername.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("plainaddress").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username..username@example.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username.@example.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@-example.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@-example.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@.com.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@.com.my").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@.com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@com.").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@example..com").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@example.com-").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@example.com.").matches());
+ assertFalse(PatternConsts.EMAIL.matcher("username@example").matches());
+ }
+
+ // ================================
+ // #endregion - EMAIL
+ // ================================
+
+ // ================================
+ // #region - Chinese2ndIdCardNumber
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "44520019900101456X",
+ "44520019900101456x",
+ "445200199001014566",
+ })
+ void testChinese2ndIdCardNumber_ValidChinese2ndIdCardNumber(String value) {
+ Matcher matcher = PatternConsts.CHINESE_2ND_ID_CARD_NUMBER.matcher(value);
+ assertTrue(matcher.matches());
+ assertEquals("44", matcher.group("province"));
+ assertEquals("4452", matcher.group("city"));
+ assertEquals("445200", matcher.group("county"));
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "4452200199001014566",
+ "44520199001014566",
+ " ",
+ "",
+ })
+ void testChinese2ndIdCardNumber_InvalidChinese2ndIdCardNumber(String value) {
+ assertFalse(PatternConsts.CHINESE_2ND_ID_CARD_NUMBER.matcher(value).matches());
+ }
+
+ // ================================
+ // #endregion - Chinese2ndIdCardNumber
+ // ================================
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/exception/test/InvalidInputExceptionTests.java b/src/test/java/xyz/zhouxy/plusone/commons/exception/test/InvalidInputExceptionTests.java
new file mode 100644
index 0000000..79a2bfc
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/exception/test/InvalidInputExceptionTests.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2024 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.exception.test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import lombok.extern.slf4j.Slf4j;
+import xyz.zhouxy.plusone.commons.exception.business.InvalidInputException;
+
+@Slf4j
+public class InvalidInputExceptionTests {
+
+ // ================================
+ // #region - createByType
+ // ================================
+
+ @Test
+ void test_createByType() {
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS.create();
+ });
+ assertSame(InvalidInputException.Type.CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS, e.getType());
+ assertEquals(InvalidInputException.Type.CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS.getCode(), e.getCode());
+ assertEquals(InvalidInputException.Type.CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS.getDefaultMessage(), e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withMessage() {
+ final String message = "test_createByType_withMessage";
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.create(message);
+ });
+ assertSame(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS, e.getType());
+ assertEquals(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withNullMessage() {
+ final String message = null;
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.PICTURE_CONTAINS_ILLEGAL_INFORMATION.create(message);
+ });
+ assertSame(InvalidInputException.Type.PICTURE_CONTAINS_ILLEGAL_INFORMATION, e.getType());
+ assertEquals(InvalidInputException.Type.PICTURE_CONTAINS_ILLEGAL_INFORMATION.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withCause() {
+ Arrays.asList("test_createByType_withCause", null).forEach(message -> {
+
+ NumberFormatException nfe = new NumberFormatException(message);
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.INFRINGE_COPYRIGHT.create(nfe);
+ });
+
+ assertSame(InvalidInputException.Type.INFRINGE_COPYRIGHT, e.getType());
+ assertEquals(InvalidInputException.Type.INFRINGE_COPYRIGHT.getCode(), e.getCode());
+ log.info("{}", e.getMessage());
+ assertEquals(nfe.toString(), e.getMessage());
+ assertSame(nfe, e.getCause());
+ });
+ }
+
+ @Test
+ void test_createByType_withNullCause() {
+ NumberFormatException nfe = null;
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.DEFAULT.create(nfe);
+ });
+
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withMessageAndCause() {
+ final String message = "test_createByType_withMessageAndCause";
+ final NumberFormatException nfe = new NumberFormatException("NumberFormatExceptionMessage");
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS.create(message, nfe);
+ });
+ assertSame(InvalidInputException.Type.CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS, e.getType());
+ assertEquals(InvalidInputException.Type.CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertSame(nfe, e.getCause());
+ }
+
+ @Test
+ void test_createByType_withNullMessageAndCause() {
+ final String message = null;
+ final NullPointerException nfe = new NullPointerException("Context is null.");
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.create(message, nfe);
+ });
+ assertSame(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS, e.getType());
+ assertEquals(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertSame(nfe, e.getCause());
+ }
+
+ @Test
+ void test_createByType_withMessageAndNullCause() {
+ final String message = "test_createByType_withMessageAndNullCause";
+ final NullPointerException npe = null;
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.create(message, npe);
+ });
+ assertSame(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS, e.getType());
+ assertEquals(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withNullMessageAndNullCause() {
+ final String message = null;
+ final NullPointerException nfe = null;
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.create(message, nfe);
+ });
+ assertSame(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS, e.getType());
+ assertEquals(InvalidInputException.Type.CONTAINS_ILLEGAL_WORDS.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ // ================================
+ // #endregion - createByType
+ // ================================
+
+ // ================================
+ // #region - constructor
+ // ================================
+
+ @Test
+ void testConstructor() {
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException();
+ });
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertEquals(InvalidInputException.Type.DEFAULT.getDefaultMessage(), e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testConstructor_withMessage() {
+ final String message = "testConstructor_withMessage";
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(message);
+ });
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testConstructor_withNullMessage() {
+ final String message = null;
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(message);
+ });
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testConstructor_withCause() {
+ Arrays.asList("testConstructor_withCause", null).forEach(message -> {
+
+ NumberFormatException nfe = new NumberFormatException(message);
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(nfe);
+ });
+
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ log.info("{}", e.getMessage());
+ assertEquals(nfe.toString(), e.getMessage());
+ assertSame(nfe, e.getCause());
+ });
+ }
+
+ @Test
+ void testConstructor_withNullCause() {
+ NumberFormatException nfe = null;
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(nfe);
+ });
+
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testConstructor_withMessageAndCause() {
+ final String message = "testConstructor_withMessageAndCause";
+ final NumberFormatException nfe = new NumberFormatException("NumberFormatExceptionMessage");
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(message, nfe);
+ });
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertSame(nfe, e.getCause());
+ }
+
+ @Test
+ void testConstructor_withNullMessageAndCause() {
+ final String message = null;
+ final NullPointerException nfe = new NullPointerException("Context is null.");
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(message, nfe);
+ });
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertSame(nfe, e.getCause());
+ }
+
+ @Test
+ void testConstructor_withMessageAndNullCause() {
+ final String message = "testConstructor_withMessageAndNullCause";
+ final NullPointerException npe = null;
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(message, npe);
+ });
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testConstructor_withNullMessageAndNullCause() {
+ final String message = null;
+ final NullPointerException nfe = null;
+
+ InvalidInputException e = assertThrows(InvalidInputException.class, () -> {
+ throw new InvalidInputException(message, nfe);
+ });
+ assertSame(InvalidInputException.Type.DEFAULT, e.getType());
+ assertEquals(InvalidInputException.Type.DEFAULT.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ // ================================
+ // #endregion - constructor
+ // ================================
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/exception/test/ParsingFailureExceptionTests.java b/src/test/java/xyz/zhouxy/plusone/commons/exception/test/ParsingFailureExceptionTests.java
new file mode 100644
index 0000000..13ecd13
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/exception/test/ParsingFailureExceptionTests.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2024 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.exception.test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import lombok.extern.slf4j.Slf4j;
+import xyz.zhouxy.plusone.commons.exception.ParsingFailureException;
+
+@Slf4j
+public class ParsingFailureExceptionTests {
+
+ // ================================
+ // #region - createByType
+ // ================================
+
+ @Test
+ void test_createByType() {
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.DATE_TIME_PARSING_FAILURE.create();
+ });
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getDefaultMessage(), e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withMessage() {
+ final String message = "test_createByType_withMessage";
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.JSON_PARSING_FAILURE.create(message);
+ });
+ assertSame(ParsingFailureException.Type.JSON_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.Type.JSON_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withNullMessage() {
+ final String message = null;
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.XML_PARSING_FAILURE.create(message);
+ });
+ assertSame(ParsingFailureException.XML_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.XML_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withCause() {
+ Arrays.asList("test_createByType_withCause", null).forEach(message -> {
+
+ NumberFormatException nfe = new NumberFormatException(message);
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.NUMBER_PARSING_FAILURE.create(nfe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ log.info("{}", e.getMessage());
+ assertEquals(nfe.toString(), e.getMessage());
+ assertSame(nfe, e.getCause());
+ });
+ }
+
+ @Test
+ void test_createByType_withNullCause() {
+ NumberFormatException nfe = null;
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.NUMBER_PARSING_FAILURE.create(nfe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withMessageAndCause() {
+ final String message = "test_createByType_withMessageAndCause";
+ final NumberFormatException nfe = new NumberFormatException("NumberFormatExceptionMessage");
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.NUMBER_PARSING_FAILURE.create(message, nfe);
+ });
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertSame(nfe, e.getCause());
+ }
+
+ @Test
+ void test_createByType_withNullMessageAndCause() {
+ final String message = null;
+ final NullPointerException nfe = new NullPointerException("Context is null.");
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.DATE_TIME_PARSING_FAILURE.create(message, nfe);
+ });
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertSame(nfe, e.getCause());
+ }
+
+ @Test
+ void test_createByType_withMessageAndNullCause() {
+ final String message = "test_createByType_withMessageAndNullCause";
+ final NullPointerException npe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.DATE_TIME_PARSING_FAILURE.create(message, npe);
+ });
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByType_withNullMessageAndNullCause() {
+ final String message = null;
+ final NullPointerException nfe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.DATE_TIME_PARSING_FAILURE.create(message, nfe);
+ });
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ // ================================
+ // #endregion - createByType
+ // ================================
+
+ // ================================
+ // #region - of DateTimeParseException
+ // ================================
+
+ @Test
+ void test_createByOf_withDateTimeParseException() {
+ DateTimeParseException dtpe = assertThrows(DateTimeParseException.class, () -> {
+ LocalDateTime.parse("abcd", DateTimeFormatter.ISO_DATE_TIME);
+ });
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(dtpe);
+ });
+
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(dtpe.getMessage(), e.getMessage());
+ assertSame(dtpe, e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNullDateTimeParseException() {
+ DateTimeParseException dtpe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(dtpe);
+ });
+
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getDefaultMessage(), e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withDateTimeParseExceptionAndMessage() {
+ final String message = "test_createByOf_withDateTimeParseExceptionAndMessage";
+ DateTimeParseException dtpe = assertThrows(DateTimeParseException.class, () -> {
+ LocalDateTime.parse("abcd", DateTimeFormatter.ISO_DATE_TIME);
+ });
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertSame(dtpe, e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withDateTimeParseExceptionAndNullMessage() {
+ final String message = null;
+ DateTimeParseException dtpe = assertThrows(DateTimeParseException.class, () -> {
+ LocalDateTime.parse("abcd", DateTimeFormatter.ISO_DATE_TIME);
+ });
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertSame(dtpe, e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNullDateTimeParseExceptionAndMessage() {
+ final String message = "test_createByOf_withDateTimeParseExceptionAndMessage";
+ DateTimeParseException dtpe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNullDateTimeParseExceptionAndNullMessage() {
+ final String message = null;
+ DateTimeParseException dtpe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.DATE_TIME_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.DATE_TIME_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ // ================================
+ // #endregion - of DateTimeParseException
+ // ================================
+
+ // ================================
+ // #region - of NumberFormatException
+ // ================================
+
+ @Test
+ void test_createByOf_withNumberFormatException() {
+ NumberFormatException dtpe = assertThrows(NumberFormatException.class, () -> {
+ Integer.parseInt("abcd");
+ });
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(dtpe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(dtpe.getMessage(), e.getMessage());
+ assertSame(dtpe, e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNullNumberFormatException() {
+ NumberFormatException dtpe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(dtpe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getDefaultMessage(), e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNumberFormatExceptionAndMessage() {
+ final String message = "test_createByOf_withNumberFormatExceptionAndMessage";
+ NumberFormatException dtpe = assertThrows(NumberFormatException.class, () -> {
+ Integer.parseInt("abcd");
+ });
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertSame(dtpe, e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNumberFormatExceptionAndNullMessage() {
+ final String message = null;
+ NumberFormatException dtpe = assertThrows(NumberFormatException.class, () -> {
+ Integer.parseInt("abcd");
+ });
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertSame(dtpe, e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNullNumberFormatExceptionAndMessage() {
+ final String message = "test_createByOf_withNumberFormatExceptionAndMessage";
+ NumberFormatException dtpe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void test_createByOf_withNullNumberFormatExceptionAndNullMessage() {
+ final String message = null;
+ NumberFormatException dtpe = null;
+
+ ParsingFailureException e = assertThrows(ParsingFailureException.class, () -> {
+ throw ParsingFailureException.of(message, dtpe);
+ });
+
+ assertSame(ParsingFailureException.NUMBER_PARSING_FAILURE, e.getType());
+ assertEquals(ParsingFailureException.NUMBER_PARSING_FAILURE.getCode(), e.getCode());
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ // ================================
+ // #endregion - of NumberFormatException
+ // ================================
+
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java
index 7002115..07421fe 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/model/Chinese2ndGenIDCardNumberTests.java
@@ -17,13 +17,15 @@
package xyz.zhouxy.plusone.commons.model;
import static org.junit.jupiter.api.Assertions.assertEquals;
+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.time.LocalDate;
import java.time.format.DateTimeParseException;
-import java.util.regex.Matcher;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import lombok.extern.slf4j.Slf4j;
@@ -31,20 +33,11 @@ import lombok.extern.slf4j.Slf4j;
public class Chinese2ndGenIDCardNumberTests {
@Test
- void testPattern() {
- Matcher matcher = Chinese2ndGenIDCardNumber.PATTERN.matcher("11010520000101111X");
- assertTrue(matcher.matches());
- for (int i = 0; i < matcher.groupCount(); i++) {
- log.info("{}: {}", i, matcher.group(i));
- }
- }
-
- @Test
- void test() throws CloneNotSupportedException {
+ void testOf_success() {
Chinese2ndGenIDCardNumber idCardNumber = Chinese2ndGenIDCardNumber.of("11010520000101111X");
assertEquals("11010520000101111X", idCardNumber.value());
assertEquals(LocalDate.of(2000, 1, 1), idCardNumber.getBirthDate());
- assertEquals(Gender.MALE, idCardNumber.getGender());
+ assertSame(Gender.MALE, idCardNumber.getGender());
assertEquals("110105", idCardNumber.getCountyCode());
assertEquals("110105000000", idCardNumber.getFullCountyCode());
@@ -56,18 +49,43 @@ public class Chinese2ndGenIDCardNumberTests {
assertEquals("北京", idCardNumber.getProvinceName());
- assertThrows(IllegalArgumentException.class,
- () -> Chinese2ndGenIDCardNumber.of("1101520000101111"));
+ assertEquals("1***************1X", idCardNumber.toDesensitizedString());
+ assertEquals("110***********111X", idCardNumber.toDesensitizedString(3, 4));
+ assertEquals("110###############", idCardNumber.toDesensitizedString('#', 3, 0));
+ assertEquals("11010520000101111X", idCardNumber.toDesensitizedString(10, 8));
- assertThrows(IllegalArgumentException.class,
- () -> Chinese2ndGenIDCardNumber.of("11010520002101111X"));
+ assertThrows(IllegalArgumentException.class, () -> idCardNumber.toDesensitizedString(-1, 5));
+ assertThrows(IllegalArgumentException.class, () -> idCardNumber.toDesensitizedString(5, -1));
+ assertThrows(IllegalArgumentException.class, () -> idCardNumber.toDesensitizedString(10, 9));
+ }
- try {
- Chinese2ndGenIDCardNumber.of("11010520002101111X");
- }
- catch (IllegalArgumentException e) {
- log.error(e.getMessage(), e);
- assertTrue(e.getCause() instanceof DateTimeParseException);
+ @Test
+ void testOf_blankValue() {
+ String[] strings = { null, "", " " };
+ for (String value : strings) {
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> Chinese2ndGenIDCardNumber.of(value));
+ assertEquals("二代居民身份证校验失败:号码为空", e.getMessage());
}
}
+ @ParameterizedTest
+ @ValueSource(strings = { "1101520000101111", "110A0520000101111X", "110105220000101111X" })
+ void testOf_mismatched(String value) {
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> Chinese2ndGenIDCardNumber.of(value));
+ assertEquals("二代居民身份证校验失败:" + value, e.getMessage());
+ }
+
+ @Test
+ void testOf_wrongBirthDate() {
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> Chinese2ndGenIDCardNumber.of("11010520002101111X"));
+ assertTrue(e.getCause() instanceof DateTimeParseException);
+ }
+
+ @Test
+ void testOf_wrongProvince() {
+ assertThrows(IllegalArgumentException.class,
+ () -> Chinese2ndGenIDCardNumber.of("99010520000101111X"));
+ }
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java
index 6fd5209..be3eb22 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/model/ValidatableStringRecordTests.java
@@ -84,7 +84,7 @@ class User {
}
@ValueObject
-class Email extends ValidatableStringRecord {
+class Email extends ValidatableStringRecord {
private Email(String value) {
super(value, PatternConsts.EMAIL);
}
@@ -96,7 +96,7 @@ class Email extends ValidatableStringRecord {
}
@ValueObject
-class Username extends ValidatableStringRecord {
+class Username extends ValidatableStringRecord {
private Username(String username) {
super(username, PatternConsts.USERNAME);
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponseTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponseTests.java
new file mode 100644
index 0000000..45a6ed2
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/UnifiedResponseTests.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2024 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 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;
+import xyz.zhouxy.plusone.commons.model.dto.UnifiedResponse.SuccessResult;
+
+@Slf4j
+public
+class UnifiedResponseTests {
+
+ static final ObjectMapper jackson = new ObjectMapper();
+
+ static final Gson gson = new Gson();
+
+ static final PageResult 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 success = UnifiedResponse.success();
+ assertEquals(SuccessResult.SUCCESS_STATUS, success.getStatus());
+ assertEquals("SUCCESS", success.getMessage());
+ assertNull(success.getData());
+ String jacksonSuccess = jackson.writeValueAsString(success);
+ log.info("jacksonSuccess: {}", jacksonSuccess);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"SUCCESS\"}", jacksonSuccess);
+ }
+
+ @Test
+ void testSuccess_WithMessage() throws Exception {
+ // 2. success with message
+ UnifiedResponse successWithMessage = UnifiedResponse.success("成功");
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithMessage.getStatus());
+ assertEquals("成功", successWithMessage.getMessage());
+ assertNull(successWithMessage.getData());
+ String jacksonSuccessWithMessage = jackson.writeValueAsString(successWithMessage);
+ log.info("jacksonSuccessWithMessage: {}", jacksonSuccessWithMessage);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"成功\"}", jacksonSuccessWithMessage);
+ }
+
+ @Test
+ void testSuccess_WithMessageAndNullData() throws Exception {
+ // success with message and null data
+ final UnifiedResponse successWithMessageAndNullData = UnifiedResponse.success("查询成功", null);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithMessageAndNullData.getStatus());
+ assertEquals("查询成功", successWithMessageAndNullData.getMessage());
+ assertNull(successWithMessageAndNullData.getData());
+ final String jacksonSuccessWithMessageAndNullData = jackson.writeValueAsString(successWithMessageAndNullData);
+ log.info("jacksonSuccessWithMessageAndNullData: {}", jacksonSuccessWithMessageAndNullData);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\"}", jacksonSuccessWithMessageAndNullData);
+
+ assertEquals("{status: \"2000000\", message: \"查询成功\", data: null}", successWithMessageAndNullData.toString());
+ }
+
+ @Test
+ void testSuccess_WithMessageAndStringData() throws Exception {
+ UnifiedResponse successWithStringData = UnifiedResponse.success("查询成功", "zhouxy");
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithStringData.getStatus());
+ assertEquals("查询成功", successWithStringData.getMessage());
+ assertEquals("zhouxy", successWithStringData.getData());
+ String jacksonSuccessWithStringData = jackson.writeValueAsString(successWithStringData);
+ log.info("jacksonSuccessWithStringData: {}", jacksonSuccessWithStringData);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":\"zhouxy\"}", jacksonSuccessWithStringData);
+
+ assertEquals("{status: \"2000000\", message: \"查询成功\", data: \"zhouxy\"}", successWithStringData.toString());
+ }
+
+ @Test
+ void testSuccess_WithMessageAndIntegerData() throws Exception {
+ final UnifiedResponse successWithIntegerData = UnifiedResponse.success("查询成功", 1);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithIntegerData.getStatus());
+ assertEquals("查询成功", successWithIntegerData.getMessage());
+ assertEquals(1, successWithIntegerData.getData());
+ final String jacksonSuccessWithIntegerData = jackson.writeValueAsString(successWithIntegerData);
+ log.info("jacksonSuccessWithIntegerData: {}", jacksonSuccessWithIntegerData);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"查询成功\",\"data\":1}", jacksonSuccessWithIntegerData);
+
+ assertEquals("{status: \"2000000\", message: \"查询成功\", data: 1}", successWithIntegerData.toString());
+ }
+
+ @Test
+ void testSuccess_WithMessageAndData() throws Exception {
+ UnifiedResponse successWithData = UnifiedResponse.success("查询成功", pageResult);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithData.getStatus());
+ assertEquals("查询成功", successWithData.getMessage());
+ assertNotNull(successWithData.getData());
+ assertEquals(pageResult, successWithData.getData());
+ String jacksonSuccessWithData = jackson.writeValueAsString(successWithData);
+ log.info("jacksonSuccessWithData: {}", jacksonSuccessWithData);
+ assertEquals("{\"status\":\"2000000\",\"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 successWithNullMessage = UnifiedResponse.success(null);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithNullMessage.getStatus());
+ assertEquals("", successWithNullMessage.getMessage());
+ assertNull(successWithNullMessage.getData());
+ String jacksonSuccessWithNullMessage = jackson.writeValueAsString(successWithNullMessage);
+ log.info("jacksonSuccessWithNullMessage: {}", jacksonSuccessWithNullMessage);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithNullMessage);
+ }
+
+ // success with null message and null data
+ @Test
+ void testSuccess_WithNullMessageAndNullData() throws Exception {
+ final UnifiedResponse successWithNullMessageAndNullData = UnifiedResponse.success(null, null);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithNullMessageAndNullData.getStatus());
+ assertEquals("", successWithNullMessageAndNullData.getMessage());
+ assertNull(successWithNullMessageAndNullData.getData());
+
+ final String jacksonSuccessWithNullMessageAndNullData = jackson.writeValueAsString(successWithNullMessageAndNullData);
+ log.info("jacksonSuccessWithNullMessageAndNullData: {}", jacksonSuccessWithNullMessageAndNullData);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithNullMessageAndNullData);
+
+ assertEquals("{status: \"2000000\", 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 successWithNullMessageAndData = UnifiedResponse.success(null, user);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithNullMessageAndData.getStatus());
+ assertEquals("", successWithNullMessageAndData.getMessage());
+ assertEquals(user, successWithNullMessageAndData.getData());
+ final String jacksonSuccessWithNullMessageAndData = jackson.writeValueAsString(successWithNullMessageAndData);
+ log.info("jacksonSuccessWithNullMessageAndData: {}", jacksonSuccessWithNullMessageAndData);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@code108.cn\"}}",
+ jacksonSuccessWithNullMessageAndData);
+ }
+
+ @Test
+ void testSuccess_WithEmptyMessage() throws Exception {
+ // 4. success with empty message
+ UnifiedResponse successWithEmptyMessage = UnifiedResponse.success("");
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithEmptyMessage.getStatus());
+ assertEquals("", successWithEmptyMessage.getMessage());
+ assertNull(successWithEmptyMessage.getData());
+ String jacksonSuccessWithEmptyMessage = jackson.writeValueAsString(successWithEmptyMessage);
+ log.info("jacksonSuccessWithEmptyMessage: {}", jacksonSuccessWithEmptyMessage);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithEmptyMessage);
+ }
+
+ // success with empty message and null data
+ @Test
+ void testSuccess_WithEmptyMessageAndNullData() throws Exception {
+ final UnifiedResponse successWithEmptyMessageAndNullData = UnifiedResponse.success("", null);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithEmptyMessageAndNullData.getStatus());
+ assertEquals("", successWithEmptyMessageAndNullData.getMessage());
+ assertNull(successWithEmptyMessageAndNullData.getData());
+
+ final String jacksonSuccessWithEmptyMessageAndNullData = jackson.writeValueAsString(successWithEmptyMessageAndNullData);
+ log.info("jacksonSuccessWithEmptyMessageAndNullData: {}", jacksonSuccessWithEmptyMessageAndNullData);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"\"}", jacksonSuccessWithEmptyMessageAndNullData);
+
+ assertEquals("{status: \"2000000\", 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 successWithEmptyMessageAndData = UnifiedResponse.success("", user);
+ assertEquals(SuccessResult.SUCCESS_STATUS, successWithEmptyMessageAndData.getStatus());
+ assertEquals("", successWithEmptyMessageAndData.getMessage());
+ assertEquals(user, successWithEmptyMessageAndData.getData());
+
+ final String jacksonSuccessWithEmptyMessageAndData = jackson.writeValueAsString(successWithEmptyMessageAndData);
+ log.info("jacksonSuccessWithEmptyMessageAndData: {}", jacksonSuccessWithEmptyMessageAndData);
+ assertEquals("{\"status\":\"2000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}", jacksonSuccessWithEmptyMessageAndData);
+ }
+
+ @Test
+ void testError_WithMessage() throws Exception {
+ UnifiedResponse errorWithMessage = UnifiedResponse.error("查询失败");
+ assertEquals("9999999", errorWithMessage.getStatus());
+ assertEquals("查询失败", errorWithMessage.getMessage());
+ assertNull(errorWithMessage.getData());
+
+ final String jacksonErrorWithMessage = jackson.writeValueAsString(errorWithMessage);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", jacksonErrorWithMessage);
+ final String gsonErrorWithMessage = gson.toJson(errorWithMessage);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", gsonErrorWithMessage);
+ }
+
+ @Test
+ void testError_WithMessage_AndNullData() throws Exception {
+ UnifiedResponse errorWithMessageAndNullData = UnifiedResponse.error("查询失败", (Object) null);
+ assertEquals("9999999", errorWithMessageAndNullData.getStatus());
+ assertEquals("查询失败", errorWithMessageAndNullData.getMessage());
+ assertNull(errorWithMessageAndNullData.getData());
+
+ final String jacksonErrorWithMessageAndNullData = jackson.writeValueAsString(errorWithMessageAndNullData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", jacksonErrorWithMessageAndNullData);
+ final String gsonErrorWithMessageAndNullData = gson.toJson(errorWithMessageAndNullData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\"}", gsonErrorWithMessageAndNullData);
+ }
+
+ @Test
+ void testError_WithMessage_AndData() throws Exception {
+ final User user = new User("zhouxy", "zhouxy@gmail.com");
+ UnifiedResponse errorWithMessageAndData = UnifiedResponse.error("查询失败", user);
+ assertEquals("9999999", errorWithMessageAndData.getStatus());
+ assertEquals("查询失败", errorWithMessageAndData.getMessage());
+ assertEquals(user, errorWithMessageAndData.getData());
+
+ final String jacksonErrorWithMessageAndData = jackson.writeValueAsString(errorWithMessageAndData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}",
+ jacksonErrorWithMessageAndData);
+ final String gsonErrorWithMessageAndData = gson.toJson(errorWithMessageAndData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"查询失败\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}",
+ gsonErrorWithMessageAndData);
+ }
+
+ @Test
+ void testError_WithNullMessage() throws Exception {
+ UnifiedResponse errorWithNullMessage = UnifiedResponse.error((String) null);
+ assertEquals("9999999", errorWithNullMessage.getStatus());
+ assertEquals("", errorWithNullMessage.getMessage());
+ assertNull(errorWithNullMessage.getData());
+
+ final String jacksonErrorWithNullMessage = jackson.writeValueAsString(errorWithNullMessage);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorWithNullMessage);
+ final String gsonErrorWithNullMessage = gson.toJson(errorWithNullMessage);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithNullMessage);
+ }
+
+ @Test
+ void testError_WithNullMessage_AndNullData() throws Exception {
+ UnifiedResponse errorWithNullMessageAndNullData = UnifiedResponse.error((String) null, (User) null);
+ assertEquals("9999999", errorWithNullMessageAndNullData.getStatus());
+ assertEquals("", errorWithNullMessageAndNullData.getMessage());
+ assertNull(errorWithNullMessageAndNullData.getData());
+
+ final String jacksonErrorWithNullMessageAndNullData = jackson.writeValueAsString(errorWithNullMessageAndNullData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorWithNullMessageAndNullData);
+ final String gsonErrorWithNullMessageAndNullData = gson.toJson(errorWithNullMessageAndNullData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithNullMessageAndNullData);
+ }
+
+ @Test
+ void testError_WithNullMessage_AndData() throws Exception {
+ final User user = new User("zhouxy1", "zhouxy1@gmail.com");
+ UnifiedResponse errorWithNullMessageAndData = UnifiedResponse.error((String) null, user);
+ assertEquals("9999999", errorWithNullMessageAndData.getStatus());
+ assertEquals("", errorWithNullMessageAndData.getMessage());
+ assertEquals(user, errorWithNullMessageAndData.getData());
+
+ final String jacksonErrorWithNullMessageAndData = jackson.writeValueAsString(errorWithNullMessageAndData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}",
+ jacksonErrorWithNullMessageAndData);
+ final String gsonErrorWithNullMessageAndData = gson.toJson(errorWithNullMessageAndData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}",
+ gsonErrorWithNullMessageAndData);
+ }
+
+ @Test
+ void testError_WithEmptyMessage() throws Exception {
+ UnifiedResponse errorWithEmptyMessage = UnifiedResponse.error("");
+ assertEquals("9999999", errorWithEmptyMessage.getStatus());
+ assertEquals("", errorWithEmptyMessage.getMessage());
+ assertNull(errorWithEmptyMessage.getData());
+ final String jacksonErrorWithEmptyMessage = jackson.writeValueAsString(errorWithEmptyMessage);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorWithEmptyMessage);
+ final String gsonErrorWithEmptyMessage = gson.toJson(errorWithEmptyMessage);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithEmptyMessage);
+ }
+
+ @Test
+ void testError_WithEmptyMessage_AndNullData() throws Exception {
+ UnifiedResponse errorWithEmptyMessageAndNullData = UnifiedResponse.error("", (User) null);
+ assertEquals("9999999", errorWithEmptyMessageAndNullData.getStatus());
+ assertEquals("", errorWithEmptyMessageAndNullData.getMessage());
+ assertNull(errorWithEmptyMessageAndNullData.getData());
+
+ final String jacksonErrorEmptyNullMessageAndNullData = jackson.writeValueAsString(errorWithEmptyMessageAndNullData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", jacksonErrorEmptyNullMessageAndNullData);
+ final String gsonErrorWithEmptyMessageAndNullData = gson.toJson(errorWithEmptyMessageAndNullData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\"}", gsonErrorWithEmptyMessageAndNullData);
+ }
+
+ @Test
+ void testError_WithEmptyMessage_AndData() throws Exception {
+ final User user = new User("zhouxy1", "zhouxy1@gmail.com");
+ UnifiedResponse errorWithEmptyMessageAndData = UnifiedResponse.error("", user);
+ assertEquals("9999999", errorWithEmptyMessageAndData.getStatus());
+ assertEquals("", errorWithEmptyMessageAndData.getMessage());
+ assertEquals(user, errorWithEmptyMessageAndData.getData());
+
+ final String jacksonErrorWithEmptyMessageAndData = jackson.writeValueAsString(errorWithEmptyMessageAndData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}",
+ jacksonErrorWithEmptyMessageAndData);
+ final String gsonErrorWithEmptyMessageAndData = gson.toJson(errorWithEmptyMessageAndData);
+ assertEquals("{\"status\":\"9999999\",\"message\":\"\",\"data\":{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"}}",
+ gsonErrorWithEmptyMessageAndData);
+ }
+
+ @Test
+ void testError_WithStatusAndMessage() throws Exception {
+ final UnifiedResponse errorWithStatusAndMessage = UnifiedResponse.error(108, "查询失败");
+ assertEquals(108, errorWithStatusAndMessage.getStatus());
+ assertEquals("查询失败", errorWithStatusAndMessage.getMessage());
+ assertNull(errorWithStatusAndMessage.getData());
+ assertEquals("{status: 108, message: \"查询失败\", data: null}", errorWithStatusAndMessage.toString());
+
+ final String jacksonErrorWithStatusAndMessage = jackson.writeValueAsString(errorWithStatusAndMessage);
+ log.info("jacksonErrorWithStatusAndMessage: {}", jacksonErrorWithStatusAndMessage);
+ assertEquals("{\"status\":108,\"message\":\"查询失败\"}", jacksonErrorWithStatusAndMessage);
+
+ final String gsonErrorWithStatusAndMessage = gson.toJson(errorWithStatusAndMessage);
+ assertEquals("{\"status\":108,\"message\":\"查询失败\"}", gsonErrorWithStatusAndMessage);
+ }
+
+ @Test
+ void testError_WithStatusAndMessage_AndNullData() throws Exception {
+ final UnifiedResponse errorWithStatusAndMessageAndNullData = UnifiedResponse.error(108, "查询失败", null);
+ assertEquals(108, errorWithStatusAndMessageAndNullData.getStatus());
+ assertEquals("查询失败", errorWithStatusAndMessageAndNullData.getMessage());
+ assertNull(errorWithStatusAndMessageAndNullData.getData());
+ assertEquals("{status: 108, message: \"查询失败\", data: null}", errorWithStatusAndMessageAndNullData.toString());
+
+ final String jacksonErrorWithStatusAndMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndMessageAndNullData);
+ log.info("jacksonErrorWithStatusAndMessage: {}", jacksonErrorWithStatusAndMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"查询失败\"}", jacksonErrorWithStatusAndMessageAndNullData);
+
+ final String gsonErrorWithStatusAndMessageAndNullData = gson.toJson(errorWithStatusAndMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"查询失败\"}", gsonErrorWithStatusAndMessageAndNullData);
+ }
+
+ @Test
+ void testError_WithStatusAndMessage_AndData() throws Exception {
+ final PageResult emptyPageResult = PageResult.empty();
+ final UnifiedResponse errorWithStatusAndMessageAndData = UnifiedResponse.error(108, "查询失败", emptyPageResult);
+ assertEquals(108, errorWithStatusAndMessageAndData.getStatus());
+ assertEquals("查询失败", errorWithStatusAndMessageAndData.getMessage());
+ assertEquals(emptyPageResult, errorWithStatusAndMessageAndData.getData());
+ assertEquals("{status: 108, message: \"查询失败\", data: PageResult [total=0, content=[]]}", errorWithStatusAndMessageAndData.toString());
+
+ final String jacksonErrorWithStatusAndMessageAndData = jackson.writeValueAsString(errorWithStatusAndMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"查询失败\",\"data\":{\"total\":0,\"content\":[]}}",
+ jacksonErrorWithStatusAndMessageAndData);
+
+ final String gsonErrorWithStatusAndMessageAndData = gson.toJson(errorWithStatusAndMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"查询失败\",\"data\":{\"total\":0,\"content\":[]}}",
+ gsonErrorWithStatusAndMessageAndData);
+ }
+
+ @Test
+ void testError_WithStatusAndNullMessage() throws Exception {
+ UnifiedResponse errorWithStatusAndNullMessage = UnifiedResponse.error(500, (String) null);
+ assertEquals(500, errorWithStatusAndNullMessage.getStatus());
+ assertEquals("", errorWithStatusAndNullMessage.getMessage());
+ assertNull(errorWithStatusAndNullMessage.getData());
+
+ final String jacksonErrorWithStatusAndNullMessage = jackson.writeValueAsString(errorWithStatusAndNullMessage);
+ assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndNullMessage);
+
+ final String gsonErrorWithStatusAndNullMessage = gson.toJson(errorWithStatusAndNullMessage);
+ assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndNullMessage);
+ }
+
+ @Test
+ void testError_WithStatusAndNullMessage_AndNullData() throws Exception {
+ UnifiedResponse errorWithStatusAndNullMessageAndNullData = UnifiedResponse.error(500, (String) null, null);
+
+ assertEquals(500, errorWithStatusAndNullMessageAndNullData.getStatus());
+ assertEquals("", errorWithStatusAndNullMessageAndNullData.getMessage());
+ assertNull(errorWithStatusAndNullMessageAndNullData.getData());
+
+ final String jacksonErrorWithStatusAndNullMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndNullMessageAndNullData);
+ assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndNullMessageAndNullData);
+
+ final String gsonErrorWithStatusAndNullMessageAndNullData = gson.toJson(errorWithStatusAndNullMessageAndNullData);
+ assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndNullMessageAndNullData);
+ }
+
+ @Test
+ void testError_WithStatusAndNullMessage_AndData() throws Exception {
+ PageResult emptyPageResult = PageResult.empty();
+ UnifiedResponse errorWithStatusAndNullMessageAndData = UnifiedResponse.error(500, (String) null, emptyPageResult);
+ assertEquals(500, errorWithStatusAndNullMessageAndData.getStatus());
+ assertEquals("", errorWithStatusAndNullMessageAndData.getMessage());
+ assertEquals(emptyPageResult, errorWithStatusAndNullMessageAndData.getData());
+ final String jacksonErrorWithStatusAndNullMessageAndData = jackson.writeValueAsString(errorWithStatusAndNullMessageAndData);
+ assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonErrorWithStatusAndNullMessageAndData);
+ final String gsonErrorWithStatusAndNullMessageAndData = gson.toJson(errorWithStatusAndNullMessageAndData);
+ assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonErrorWithStatusAndNullMessageAndData);
+ }
+
+ @Test
+ void testError_WithStatusAndEmptyMessage() throws Exception {
+ UnifiedResponse errorWithStatusAndEmptyMessage = UnifiedResponse.error(500, "");
+ assertEquals(500, errorWithStatusAndEmptyMessage.getStatus());
+ assertEquals("", errorWithStatusAndEmptyMessage.getMessage());
+ assertNull(errorWithStatusAndEmptyMessage.getData());
+
+ final String jacksonErrorWithStatusAndEmptyMessage = jackson.writeValueAsString(errorWithStatusAndEmptyMessage);
+ assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndEmptyMessage);
+
+ final String gsonErrorWithStatusAndEmptyMessage = gson.toJson(errorWithStatusAndEmptyMessage);
+ assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndEmptyMessage);
+ }
+
+ @Test
+ void testError_WithStatusAndEmptyMessage_AndNullData() throws Exception {
+ UnifiedResponse errorWithStatusAndEmptyMessageAndNullData = UnifiedResponse.error(500, "", null);
+
+ assertEquals(500, errorWithStatusAndEmptyMessageAndNullData.getStatus());
+ assertEquals("", errorWithStatusAndEmptyMessageAndNullData.getMessage());
+ assertNull(errorWithStatusAndEmptyMessageAndNullData.getData());
+
+ final String jacksonErrorWithStatusAndEmptyMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndEmptyMessageAndNullData);
+ assertEquals("{\"status\":500,\"message\":\"\"}", jacksonErrorWithStatusAndEmptyMessageAndNullData);
+
+ final String gsonErrorWithStatusAndEmptyMessageAndNullData = gson.toJson(errorWithStatusAndEmptyMessageAndNullData);
+ assertEquals("{\"status\":500,\"message\":\"\"}", gsonErrorWithStatusAndEmptyMessageAndNullData);
+ }
+
+ @Test
+ void testError_WithStatusAndEmptyMessage_AndData() throws Exception {
+ PageResult emptyPageResult = PageResult.empty();
+ UnifiedResponse errorWithStatusAndEmptyMessageAndData = UnifiedResponse.error(500, "", emptyPageResult);
+ assertEquals(500, errorWithStatusAndEmptyMessageAndData.getStatus());
+ assertEquals("", errorWithStatusAndEmptyMessageAndData.getMessage());
+ assertEquals(emptyPageResult, errorWithStatusAndEmptyMessageAndData.getData());
+ final String jacksonErrorWithStatusAndEmptyMessageAndData = jackson.writeValueAsString(errorWithStatusAndEmptyMessageAndData);
+ assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonErrorWithStatusAndEmptyMessageAndData);
+ final String gsonErrorWithStatusAndEmptyMessageAndData = gson.toJson(errorWithStatusAndEmptyMessageAndData);
+ assertEquals("{\"status\":500,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonErrorWithStatusAndEmptyMessageAndData);
+ }
+
+ @Test
+ void testError_WithStatusAndThrowable() throws Exception {
+ final IllegalArgumentException e = new IllegalArgumentException("ID cannot be null");
+ final UnifiedResponse errorWithStatusThrowable = UnifiedResponse.error(500, e);
+ assertEquals(500, errorWithStatusThrowable.getStatus());
+ assertEquals("ID cannot be null", errorWithStatusThrowable.getMessage());
+ assertNull(errorWithStatusThrowable.getData());
+ assertEquals("{\"status\":500,\"message\":\"ID cannot be null\"}", jackson.writeValueAsString(errorWithStatusThrowable));
+ assertEquals("{\"status\":500,\"message\":\"ID cannot be null\"}", gson.toJson(errorWithStatusThrowable));
+ }
+
+ @Test
+ void testError_WithStatusAndNullThrowable() {
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.error(500, (Throwable) null));
+ }
+
+ @Test
+ void testError_WithNullStatus() {
+ final Object nullStatus = null;
+ final String nullMessage = null;
+ 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));
+
+ // empty message
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, ""));
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "", null));
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.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));
+
+ // Throwable
+ BizException bizException = new BizException("业务异常");
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, bizException));
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, (Throwable) null));
+ }
+
+
+ @Test
+ void testOf_WithStatusAndMessage() throws Exception {
+ final UnifiedResponse ofWithStatusAndMessage = UnifiedResponse.of(108, "This is a message.");
+ assertEquals(108, ofWithStatusAndMessage.getStatus());
+ assertEquals("This is a message.", ofWithStatusAndMessage.getMessage());
+ assertNull(ofWithStatusAndMessage.getData());
+
+ final String jacksonOfWithStatusAndMessage = jackson.writeValueAsString(ofWithStatusAndMessage);
+ log.info("jacksonOfWithStatusAndMessage: {}", jacksonOfWithStatusAndMessage);
+ assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", jacksonOfWithStatusAndMessage);
+
+ assertEquals("{status: 108, message: \"This is a message.\", data: null}", ofWithStatusAndMessage.toString());
+
+ final String gsonOfWithStatusAndMessage = gson.toJson(ofWithStatusAndMessage);
+ assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", gsonOfWithStatusAndMessage);
+ }
+
+ @Test
+ void testOf_WithStatusAndMessage_AndNullData() throws Exception {
+ final UnifiedResponse ofWithStatusAndMessageAndNullData = UnifiedResponse.of(108, "This is a message.", null);
+ assertEquals(108, ofWithStatusAndMessageAndNullData.getStatus());
+ assertEquals("This is a message.", ofWithStatusAndMessageAndNullData.getMessage());
+ assertNull(ofWithStatusAndMessageAndNullData.getData());
+
+ final String jacksonOfWithStatusAndMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndMessageAndNullData);
+ log.info("jacksonOfWithStatusAndMessage: {}", jacksonOfWithStatusAndMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", jacksonOfWithStatusAndMessageAndNullData);
+
+ assertEquals("{status: 108, message: \"This is a message.\", data: null}", ofWithStatusAndMessageAndNullData.toString());
+
+ final String gsonOfWithStatusAndMessageAndNullData = gson.toJson(ofWithStatusAndMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"This is a message.\"}", gsonOfWithStatusAndMessageAndNullData);
+ }
+
+ @Test
+ void testOf_WithStatusAndMessage_AndData() throws Exception {
+ final PageResult emptyPageResult = PageResult.empty();
+ final UnifiedResponse ofWithStatusAndMessageAndData
+ = UnifiedResponse.of(108, "This is a message.", emptyPageResult);
+ assertEquals("{status: 108, message: \"This is a message.\", data: PageResult [total=0, content=[]]}",
+ ofWithStatusAndMessageAndData.toString());
+ assertEquals(108, ofWithStatusAndMessageAndData.getStatus());
+ assertEquals("This is a message.", ofWithStatusAndMessageAndData.getMessage());
+ assertEquals(emptyPageResult, ofWithStatusAndMessageAndData.getData());
+ final String jacksonOfWithStatusAndMessageAndData = jackson.writeValueAsString(ofWithStatusAndMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"This is a message.\",\"data\":{\"total\":0,\"content\":[]}}",
+ jacksonOfWithStatusAndMessageAndData);
+ final String gsonOfWithStatusAndMessageAndData = gson.toJson(ofWithStatusAndMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"This is a message.\",\"data\":{\"total\":0,\"content\":[]}}",
+ gsonOfWithStatusAndMessageAndData);
+ }
+
+ @Test
+ void testOf_WithStatusAndNullMessage() throws Exception {
+ UnifiedResponse ofWithStatusAndNullMessage = UnifiedResponse.of(108, (String) null);
+ assertEquals(108, ofWithStatusAndNullMessage.getStatus());
+ assertEquals("", ofWithStatusAndNullMessage.getMessage());
+ assertNull(ofWithStatusAndNullMessage.getData());
+
+ final String jacksonOfWithStatusAndNullMessage = jackson.writeValueAsString(ofWithStatusAndNullMessage);
+ assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndNullMessage);
+
+ final String gsonOfWithStatusAndNullMessage = gson.toJson(ofWithStatusAndNullMessage);
+ assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndNullMessage);
+ }
+
+ @Test
+ void testOf_WithStatusAndNullMessage_AndNullData() throws Exception {
+ UnifiedResponse ofWithStatusAndNullMessageAndNullData = UnifiedResponse.of(108, (String) null, null);
+
+ assertEquals(108, ofWithStatusAndNullMessageAndNullData.getStatus());
+ assertEquals("", ofWithStatusAndNullMessageAndNullData.getMessage());
+ assertNull(ofWithStatusAndNullMessageAndNullData.getData());
+
+ final String jacksonOfWithStatusAndNullMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndNullMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndNullMessageAndNullData);
+
+ final String gsonOfWithStatusAndNullMessageAndNullData = gson.toJson(ofWithStatusAndNullMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndNullMessageAndNullData);
+ }
+
+ @Test
+ void testOf_WithStatusAndNullMessage_AndData() throws Exception {
+ PageResult emptyPageResult = PageResult.empty();
+ UnifiedResponse ofWithStatusAndNullMessageAndData = UnifiedResponse.of(108, (String) null, emptyPageResult);
+ assertEquals(108, ofWithStatusAndNullMessageAndData.getStatus());
+ assertEquals("", ofWithStatusAndNullMessageAndData.getMessage());
+ assertEquals(emptyPageResult, ofWithStatusAndNullMessageAndData.getData());
+ final String jacksonOfWithStatusAndNullMessageAndData = jackson.writeValueAsString(ofWithStatusAndNullMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonOfWithStatusAndNullMessageAndData);
+ final String gsonOfWithStatusAndNullMessageAndData = gson.toJson(ofWithStatusAndNullMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonOfWithStatusAndNullMessageAndData);
+ }
+
+ @Test
+ void testOf_WithStatusAndEmptyMessage() throws Exception {
+ UnifiedResponse ofWithStatusAndEmptyMessage = UnifiedResponse.of(108, "");
+ assertEquals(108, ofWithStatusAndEmptyMessage.getStatus());
+ assertEquals("", ofWithStatusAndEmptyMessage.getMessage());
+ assertNull(ofWithStatusAndEmptyMessage.getData());
+
+ final String jacksonOfWithStatusAndEmptyMessage = jackson.writeValueAsString(ofWithStatusAndEmptyMessage);
+ assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndEmptyMessage);
+
+ final String gsonOfWithStatusAndEmptyMessage = gson.toJson(ofWithStatusAndEmptyMessage);
+ assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndEmptyMessage);
+ }
+
+ @Test
+ void testOf_WithStatusAndEmptyMessage_AndNullData() throws Exception {
+ UnifiedResponse ofWithStatusAndEmptyMessageAndNullData = UnifiedResponse.of(108, "", null);
+
+ assertEquals(108, ofWithStatusAndEmptyMessageAndNullData.getStatus());
+ assertEquals("", ofWithStatusAndEmptyMessageAndNullData.getMessage());
+ assertNull(ofWithStatusAndEmptyMessageAndNullData.getData());
+
+ final String jacksonOfWithStatusAndEmptyMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndEmptyMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"\"}", jacksonOfWithStatusAndEmptyMessageAndNullData);
+
+ final String gsonOfWithStatusAndEmptyMessageAndNullData = gson.toJson(ofWithStatusAndEmptyMessageAndNullData);
+ assertEquals("{\"status\":108,\"message\":\"\"}", gsonOfWithStatusAndEmptyMessageAndNullData);
+ }
+
+ @Test
+ void testOf_WithStatusAndEmptyMessage_AndData() throws Exception {
+ PageResult emptyPageResult = PageResult.empty();
+ UnifiedResponse ofWithStatusAndEmptyMessageAndData = UnifiedResponse.of(108, "", emptyPageResult);
+ assertEquals(108, ofWithStatusAndEmptyMessageAndData.getStatus());
+ assertEquals("", ofWithStatusAndEmptyMessageAndData.getMessage());
+ assertEquals(emptyPageResult, ofWithStatusAndEmptyMessageAndData.getData());
+ final String jacksonOfWithStatusAndEmptyMessageAndData = jackson.writeValueAsString(ofWithStatusAndEmptyMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonOfWithStatusAndEmptyMessageAndData);
+ final String gsonOfWithStatusAndEmptyMessageAndData = gson.toJson(ofWithStatusAndEmptyMessageAndData);
+ assertEquals("{\"status\":108,\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonOfWithStatusAndEmptyMessageAndData);
+ }
+
+ @Test
+ void testOf_WithNullStatus() {
+ final Object nullStatus = null;
+ final String nullMessage = null;
+ 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));
+
+ // empty message
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, ""));
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "", null));
+ assertThrows(NullPointerException.class, () -> UnifiedResponse.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));
+ }
+
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ private static class User {
+ private String username;
+ private String email;
+ }
+
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/model/dto/test/PagingAndSortingQueryParamsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/test/PagingAndSortingQueryParamsTests.java
new file mode 100644
index 0000000..2810acc
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/model/dto/test/PagingAndSortingQueryParamsTests.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2024 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.test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.Statement;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.h2.jdbcx.JdbcDataSource;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+import xyz.zhouxy.plusone.commons.model.dto.PageResult;
+import xyz.zhouxy.plusone.commons.model.dto.PagingAndSortingQueryParams;
+import xyz.zhouxy.plusone.commons.model.dto.PagingParams;
+
+@Slf4j
+public class PagingAndSortingQueryParamsTests {
+
+ static SqlSessionFactory sqlSessionFactory;
+
+ @BeforeAll
+ static void setUp() throws Exception {
+ initDatabase();
+
+ String resource = "mybatis-config.xml";
+ InputStream inputStream = Resources.getResourceAsStream(resource);
+ sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
+ }
+
+ static void initDatabase() throws Exception {
+ JdbcDataSource dataSource = new JdbcDataSource();
+ dataSource.setURL("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=FALSE;MODE=MySQL");
+ dataSource.setUser("sa");
+ dataSource.setPassword("");
+
+ List data = Lists.newArrayList(
+ new AccountVO(1L, "zhouxy01", "zhouxy01@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 1, 13, 15), 0L),
+ new AccountVO(2L, "zhouxy02", "zhouxy02@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 2, 13, 15), 0L),
+ new AccountVO(3L, "zhouxy03", "zhouxy03@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 3, 13, 15), 0L),
+ new AccountVO(4L, "zhouxy04", "zhouxy04@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 4, 13, 15), 0L),
+ new AccountVO(5L, "zhouxy05", "zhouxy05@qq.com", 0, 108L, LocalDateTime.of(2020, 1, 5, 13, 15), 0L),
+ new AccountVO(6L, "zhouxy06", "zhouxy06@qq.com", 0, 108L, LocalDateTime.of(2024, 1, 6, 13, 15), 0L),
+ new AccountVO(7L, "zhouxy07", "zhouxy07@qq.com", 0, 108L, LocalDateTime.of(2024, 1, 7, 13, 15), 0L),
+ new AccountVO(8L, "zhouxy08", "zhouxy08@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 8, 13, 15), 0L),
+ new AccountVO(9L, "zhouxy09", "zhouxy09@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 9, 13, 15), 0L),
+ new AccountVO(10L, "zhouxy10", "zhouxy10@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 10, 13, 15), 0L),
+ new AccountVO(11L, "zhouxy11", "zhouxy11@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 11, 13, 15), 0L),
+ new AccountVO(12L, "zhouxy12", "zhouxy12@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 12, 13, 15), 0L),
+ new AccountVO(13L, "zhouxy13", "zhouxy13@qq.com", 1, 108L, LocalDateTime.of(2024, 5, 13, 13, 15), 0L),
+ new AccountVO(14L, "zhouxy14", "zhouxy14@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 14, 13, 15), 0L),
+ new AccountVO(15L, "zhouxy15", "zhouxy15@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 15, 13, 15), 0L),
+ new AccountVO(16L, "zhouxy16", "zhouxy16@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 16, 13, 15), 0L),
+ new AccountVO(17L, "zhouxy17", "zhouxy17@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 17, 13, 15), 0L),
+ new AccountVO(18L, "zhouxy18", "zhouxy18@qq.com", 1, 108L, LocalDateTime.of(2024, 10, 18, 13, 15), 0L),
+ new AccountVO(19L, "zhouxy19", "zhouxy19@qq.com", 1, 108L, LocalDateTime.of(2024, 11, 19, 13, 15), 0L),
+ new AccountVO(20L, "zhouxy20", "zhouxy20@qq.com", 1, 108L, LocalDateTime.of(2024, 12, 20, 13, 15), 0L)
+ );
+
+ try (Connection conn = dataSource.getConnection()) {
+ try (Statement statement = conn.createStatement()) {
+ String ddl = "CREATE TABLE sys_account ("
+ + "\n" + " id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY"
+ + "\n" + " ,username VARCHAR(255) NOT NULL"
+ + "\n" + " ,email VARCHAR(255) NOT NULL"
+ + "\n" + " ,status VARCHAR(2) NOT NULL"
+ + "\n" + " ,create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP"
+ + "\n" + " ,created_by BIGINT NOT NULL"
+ + "\n" + " ,version BIGINT NOT NULL DEFAULT 0"
+ + "\n" + ")";
+ statement.execute(ddl);
+ }
+
+ String sql = "INSERT INTO sys_account(id, username, email, status, create_time, created_by, version) VALUES"
+ + "\n" + "(?, ?, ?, ?, ?, ?, ?)";
+ try (PreparedStatement statement = conn.prepareStatement(sql)) {
+ for (AccountVO a : data) {
+ statement.setObject(1, a.getId());
+ statement.setObject(2, a.getUsername());
+ statement.setObject(3, a.getEmail());
+ statement.setObject(4, a.getStatus());
+ statement.setObject(5, a.getCreateTime());
+ statement.setObject(6, a.getCreatedBy());
+ statement.setObject(7, a.getVersion());
+ statement.addBatch();
+ }
+ statement.executeBatch();
+ statement.clearBatch();
+ }
+ }
+ }
+
+ static final String JSON_STR = "" +
+ "{\n" +
+ " \"pageNum\": 3,\n" +
+ " \"size\": 3,\n" +
+ " \"orderBy\": [\"username-asc\"],\n" +
+ " \"createTimeStart\": \"2024-05-06\",\n" +
+ " \"createTimeEnd\": \"2030-07-06\"" +
+ "}";
+
+ static final String WRONG_JSON_STR = "" +
+ "{\n" +
+ " \"pageNum\": 3,\n" +
+ " \"size\": 3,\n" +
+ " \"orderBy\": [\"status-asc\"],\n" +
+ " \"createTimeStart\": \"2024-05-06\",\n" +
+ " \"createTimeEnd\": \"2030-07-06\"" +
+ "}";
+
+ @Test
+ void testJackson() throws Exception {
+ ObjectMapper jackson = new ObjectMapper();
+ jackson.registerModule(new JavaTimeModule());
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ AccountQueryParams params = jackson.readValue(JSON_STR, AccountQueryParams.class);
+ PagingParams pagingParams = params.buildPagingParams();
+
+ AccountQueries accountQueries = session.getMapper(AccountQueries.class);
+ List list = accountQueries.queryAccountList(params, pagingParams);
+ long count = accountQueries.countAccount(params);
+ PageResult accountPageResult = PageResult.of(list, count);
+ log.info(jackson.writeValueAsString(accountPageResult));
+
+ assertEquals(Lists.newArrayList(
+ new AccountVO(14L, "zhouxy14", "zhouxy14@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 14, 13, 15), 0L),
+ new AccountVO(15L, "zhouxy15", "zhouxy15@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 15, 13, 15), 0L),
+ new AccountVO(16L, "zhouxy16", "zhouxy16@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 16, 13, 15), 0L)
+ ), accountPageResult.getContent());
+ assertEquals(13, accountPageResult.getTotal());
+ } catch (Exception e) {
+ log.error("测试不通过", e);
+ throw e;
+ }
+
+ AccountQueryParams queryParams = jackson.readValue(WRONG_JSON_STR, AccountQueryParams.class);
+ assertThrows(IllegalArgumentException.class, () -> queryParams.buildPagingParams()); // NOSONAR
+ }
+
+ static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+ @Test
+ void testGson() throws Exception {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(LocalDate.class, new TypeAdapter() {
+
+ @Override
+ public void write(JsonWriter out, LocalDate value) throws IOException {
+ out.value(dateFormatter.format(value));
+ }
+
+ @Override
+ public LocalDate read(JsonReader in) throws IOException {
+ return LocalDate.parse(in.nextString(), dateFormatter);
+ }
+
+ })
+ .create();
+ try (SqlSession session = sqlSessionFactory.openSession()) {
+ AccountQueryParams params = gson.fromJson(JSON_STR, AccountQueryParams.class);
+ log.info(params.toString());
+ PagingParams pagingParams = params.buildPagingParams();
+ log.info("pagingParams: {}", pagingParams);
+ AccountQueries accountQueries = session.getMapper(AccountQueries.class);
+ List list = accountQueries.queryAccountList(params, pagingParams);
+ long count = accountQueries.countAccount(params);
+ PageResult accountPageResult = PageResult.of(list, count);
+ log.info(gson.toJson(accountPageResult));
+
+ assertEquals(Lists.newArrayList(
+ new AccountVO(14L, "zhouxy14", "zhouxy14@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 14, 13, 15), 0L),
+ new AccountVO(15L, "zhouxy15", "zhouxy15@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 15, 13, 15), 0L),
+ new AccountVO(16L, "zhouxy16", "zhouxy16@qq.com", 1, 108L, LocalDateTime.of(2024, 8, 16, 13, 15), 0L)
+ ), accountPageResult.getContent());
+ assertEquals(13, accountPageResult.getTotal());
+ } catch (Exception e) {
+ log.error("测试不通过", e);
+ throw e;
+ }
+
+ AccountQueryParams queryParams = gson.fromJson(WRONG_JSON_STR, AccountQueryParams.class);
+ assertThrows(IllegalArgumentException.class, () -> queryParams.buildPagingParams()); // NOSONAR
+ }
+}
+
+
+/**
+ * 账号信息查询参数
+ *
+ * @author ZhouXY
+ */
+@ToString(callSuper = true)
+class AccountQueryParams extends PagingAndSortingQueryParams {
+
+ private static final Map PROPERTY_COLUMN_MAP = ImmutableMap.builder()
+ .put("id", "id")
+ .put("username", "username")
+ .put("createTime", "create_time")
+ .build();
+
+ public AccountQueryParams() {
+ super(PROPERTY_COLUMN_MAP);
+ }
+
+ private @Getter @Setter Long id;
+ private @Getter @Setter String username;
+ private @Getter @Setter String email;
+ private @Getter @Setter Integer status;
+ private @Getter @Setter Long createdBy;
+ private @Getter @Setter LocalDate createTimeStart;
+ private @Setter LocalDate createTimeEnd;
+
+ public LocalDate getCreateTimeEnd() {
+ if (this.createTimeEnd == null) {
+ return null;
+ }
+ return this.createTimeEnd.plusDays(1);
+ }
+}
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode
+class AccountVO {
+ private Long id;
+ private String username;
+ private String email;
+ private Integer status;
+ private Long createdBy;
+ private LocalDateTime createTime;
+ private Long version;
+}
+
+interface AccountQueries {
+
+ List queryAccountList(@Param("query") AccountQueryParams query,
+ @Param("page") PagingParams page);
+
+ long countAccount(@Param("query") AccountQueryParams query);
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/queryparams/test/PagingAndSortingQueryParamsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/queryparams/test/PagingAndSortingQueryParamsTests.java
deleted file mode 100644
index d6241bc..0000000
--- a/src/test/java/xyz/zhouxy/plusone/commons/queryparams/test/PagingAndSortingQueryParamsTests.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2024 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.queryparams.test;
-
-import java.io.IOException;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.Map;
-
-import org.junit.jupiter.api.Test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import com.google.common.collect.ImmutableMap;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.TypeAdapter;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-import lombok.extern.slf4j.Slf4j;
-import xyz.zhouxy.plusone.commons.model.dto.PagingAndSortingQueryParams;
-import xyz.zhouxy.plusone.commons.model.dto.PagingParams;
-
-@Slf4j
-public //
-class PagingAndSortingQueryParamsTests {
- static final String JSON_STR = "" +
- "{\n" +
- " \"size\": 15,\n" +
- " \"orderBy\": [\"username-asc\"],\n" +
- " \"createTimeStart\": \"2024-05-06\",\n" +
- " \"createTimeEnd\": \"2024-07-06\",\n" +
- " \"updateTimeStart\": \"2024-08-06\",\n" +
- " \"updateTimeEnd\": \"2024-10-06\",\n" +
- " \"mobilePhone\": \"13169053215\"\n" +
- "}";
-
- @Test
- void testJackson() throws Exception {
- try {
- ObjectMapper om = new ObjectMapper();
- om.registerModule(new JavaTimeModule());
- AccountQueryParams params = om.readValue(JSON_STR, AccountQueryParams.class);
- log.info(params.toString());
- PagingParams pagingParams = params.buildPagingParams();
- log.info("pagingParams: {}", pagingParams);
- } catch (Exception e) {
- log.error("测试不通过", e);
- throw e;
- }
- }
-
- static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
-
- static final TypeAdapter dateAdapter = new TypeAdapter() {
-
- @Override
- public void write(JsonWriter out, LocalDate value) throws IOException {
- out.value(dateFormatter.format(value));
- }
-
- @Override
- public LocalDate read(JsonReader in) throws IOException {
- return LocalDate.parse(in.nextString(), dateFormatter);
- }
-
- };
-
- @Test
- void testGson() throws Exception {
- try {
- Gson gson = new GsonBuilder()
- .registerTypeAdapter(LocalDate.class, dateAdapter)
- .create();
- AccountQueryParams params = gson.fromJson(JSON_STR, AccountQueryParams.class);
- log.info(params.toString());
- PagingParams pagingParams = params.buildPagingParams();
- log.info("pagingParams: {}", pagingParams);
- } catch (Exception e) {
- log.error("测试不通过", e);
- throw e;
- }
- }
-}
-
-
-/**
- * 账号信息查询参数
- *
- * @author ZhouXY
- */
-@ToString(callSuper = true)
-class AccountQueryParams extends PagingAndSortingQueryParams {
-
- private static final Map PROPERTY_COLUMN_MAP = ImmutableMap.builder()
- .put("id", "id")
- .put("username", "username")
- .put("email", "email")
- .put("mobilePhone", "mobile_phone")
- .put("status", "status")
- .put("nickname", "nickname")
- .put("sex", "sex")
- .put("createdBy", "created_by")
- .put("createTime", "create_time")
- .put("updatedBy", "updated_by")
- .put("updateTime", "update_time")
- .build();
-
- public AccountQueryParams() {
- super(PROPERTY_COLUMN_MAP);
- }
-
- private @Getter @Setter Long id;
- private @Getter @Setter String username;
- private @Getter @Setter String email;
- private @Getter @Setter String mobilePhone;
- private @Getter @Setter Integer status;
- private @Getter @Setter String nickname;
- private @Getter @Setter Integer sex;
- private @Getter @Setter Long createdBy;
- private @Getter @Setter LocalDate createTimeStart;
- private @Setter LocalDate createTimeEnd;
- private @Getter @Setter Long updatedBy;
- private @Getter @Setter LocalDate updateTimeStart;
- private @Setter LocalDate updateTimeEnd;
- private @Getter @Setter Long roleId;
-
- public LocalDate getCreateTimeEnd() {
- if (this.createTimeEnd == null) {
- return null;
- }
- return this.createTimeEnd.plusDays(1);
- }
-
- public LocalDate getUpdateTimeEnd() {
- if (this.updateTimeEnd == null) {
- return null;
- }
- return this.updateTimeEnd.plusDays(1);
- }
-}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java b/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java
index 69637a8..976ccc7 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/time/QuarterTests.java
@@ -18,6 +18,7 @@ package xyz.zhouxy.plusone.commons.time;
import static org.junit.jupiter.api.Assertions.*;
+import java.time.DateTimeException;
import java.time.Month;
import java.time.MonthDay;
@@ -37,7 +38,7 @@ class QuarterTests {
assertEquals(1, quarter.getValue());
assertEquals("Q1", quarter.name());
- assertThrows(IllegalArgumentException.class, () -> {
+ assertThrows(DateTimeException.class, () -> {
Quarter.of(0);
});
@@ -84,7 +85,7 @@ class QuarterTests {
assertEquals(2, quarter.getValue());
assertEquals("Q2", quarter.name());
- assertThrows(IllegalArgumentException.class, () -> {
+ assertThrows(DateTimeException.class, () -> {
Quarter.of(5);
});
@@ -240,4 +241,116 @@ class QuarterTests {
firstDayOfYear = Quarter.Q4.firstDayOfYear(false);
assertEquals(1 + (31 + 28 + 31) + (30 + 31 + 30) + (31 + 30 + 31), firstDayOfYear);
}
+
+ @Test
+ void testPlusZeroAndPositiveRealNumbers() {
+ for (int i = 0; i < 100; i += 4) {
+ assertEquals(Quarter.Q1, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q2, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q3, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q4, Quarter.Q4.plus(i));
+ }
+ for (int i = 1; i < 100 + 1; i += 4) {
+ assertEquals(Quarter.Q2, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q3, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q4, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q1, Quarter.Q4.plus(i));
+ }
+ for (int i = 2; i < 100 + 2; i += 4) {
+ assertEquals(Quarter.Q3, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q4, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q1, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q2, Quarter.Q4.plus(i));
+ }
+ for (int i = 3; i < 100 + 3; i += 4) {
+ assertEquals(Quarter.Q4, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q1, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q2, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q3, Quarter.Q4.plus(i));
+ }
+ }
+
+ @Test
+ void testPlusZeroAndNegativeNumber() {
+ for (int i = 0; i > -100; i -= 4) {
+ assertEquals(Quarter.Q1, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q2, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q3, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q4, Quarter.Q4.plus(i));
+ }
+ for (int i = -1; i > -(100 + 1); i -= 4) {
+ assertEquals(Quarter.Q4, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q1, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q2, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q3, Quarter.Q4.plus(i));
+ }
+ for (int i = -2; i > -(100 + 2); i -= 4) {
+ assertEquals(Quarter.Q3, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q4, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q1, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q2, Quarter.Q4.plus(i));
+ }
+ for (int i = -3; i > -(100 + 3); i -= 4) {
+ assertEquals(Quarter.Q2, Quarter.Q1.plus(i));
+ assertEquals(Quarter.Q3, Quarter.Q2.plus(i));
+ assertEquals(Quarter.Q4, Quarter.Q3.plus(i));
+ assertEquals(Quarter.Q1, Quarter.Q4.plus(i));
+ }
+ }
+
+ @Test
+ void testMinusZeroAndNegativeNumber() {
+ for (int i = 0; i < 100; i += 4) {
+ assertEquals(Quarter.Q1, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q2, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q3, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q4, Quarter.Q4.minus(i));
+ }
+ for (int i = 1; i < 100 + 1; i += 4) {
+ assertEquals(Quarter.Q4, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q1, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q2, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q3, Quarter.Q4.minus(i));
+ }
+ for (int i = 2; i < 100 + 2; i += 4) {
+ assertEquals(Quarter.Q3, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q4, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q1, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q2, Quarter.Q4.minus(i));
+ }
+ for (int i = 3; i < 100 + 3; i += 4) {
+ assertEquals(Quarter.Q2, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q3, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q4, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q1, Quarter.Q4.minus(i));
+ }
+ }
+
+ @Test
+ void testMinusZeroAndPositiveRealNumbers() {
+ for (int i = 0; i > -100; i -= 4) {
+ assertEquals(Quarter.Q1, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q2, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q3, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q4, Quarter.Q4.minus(i));
+ }
+ for (int i = -1; i > -(100 + 1); i -= 4) {
+ assertEquals(Quarter.Q2, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q3, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q4, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q1, Quarter.Q4.minus(i));
+ }
+ for (int i = -2; i > -(100 + 2); i -= 4) {
+ assertEquals(Quarter.Q3, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q4, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q1, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q2, Quarter.Q4.minus(i));
+ }
+ for (int i = -3; i > -(100 + 3); i -= 4) {
+ assertEquals(Quarter.Q4, Quarter.Q1.minus(i));
+ assertEquals(Quarter.Q1, Quarter.Q2.minus(i));
+ assertEquals(Quarter.Q2, Quarter.Q3.minus(i));
+ assertEquals(Quarter.Q3, Quarter.Q4.minus(i));
+ }
+ }
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java b/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java
index 0583839..fcb14a6 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/time/YearQuarterTests.java
@@ -21,66 +21,775 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import lombok.extern.slf4j.Slf4j;
+import java.time.DateTimeException;
import java.time.LocalDate;
+import java.time.Month;
+import java.time.Year;
import java.time.YearMonth;
-
+import java.util.Calendar;
+import java.util.Date;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Slf4j
public class YearQuarterTests {
- @Test
- void of_ValidYearAndQuarter_CreatesYearQuarter() {
- int year = 2023;
- Quarter quarter = Quarter.Q1;
+ // ================================
+ // #region - of(int year, int quarter)
+ // ================================
- YearQuarter expected = YearQuarter.of(year, quarter);
- YearQuarter actual = YearQuarter.of(LocalDate.of(year, 2, 28));
+ @ParameterizedTest
+ @ValueSource(ints = { 1, 2, 3, 4 })
+ void of_ValidYearAndQuarterValue_CreatesYearQuarter(int quarter) {
+ {
+ int year = 2024;
+ YearQuarter yearQuarter = YearQuarter.of(year, quarter);
+ log.info("{}", yearQuarter);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(Quarter.of(quarter), yearQuarter.getQuarter());
+ assertEquals(quarter, yearQuarter.getQuarterValue());
+ }
+ {
+ int year = Year.MIN_VALUE;
+ YearQuarter yearQuarter = YearQuarter.of(year, quarter);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(Quarter.of(quarter), yearQuarter.getQuarter());
+ assertEquals(quarter, yearQuarter.getQuarterValue());
+ }
+ {
+ int year = Year.MAX_VALUE;
+ YearQuarter yearQuarter = YearQuarter.of(year, quarter);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(Quarter.of(quarter), yearQuarter.getQuarter());
+ assertEquals(quarter, yearQuarter.getQuarterValue());
+ }
+ }
- assertEquals(expected, actual);
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 0, 5, 108 })
+ void of_ValidYearAndInvalidQuarterValue_DateTimeException(int quarter) {
+ int year = 2024;
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, quarter);
+ });
+ }
- assertEquals("2023 Q1", actual.toString());
+ @ParameterizedTest
+ @ValueSource(ints = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 })
+ void of_InvalidYearAndValidQuarterValue_DateTimeException(int year) {
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, 1);
+ });
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, 2);
+ });
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, 3);
+ });
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, 4);
+ });
}
@Test
- @SuppressWarnings("null")
- void of_InvalidQuarter_ThrowsNullPointerException() {
- int year = 2023;
- Quarter quarter = null;
-
- assertThrows(NullPointerException.class, () -> YearQuarter.of(year, quarter));
- }
-
- @Test
- void of_ValidYearQuarter_GetsCorrectStartAndEndDate() {
-
- for (int year = 1990; year <= 2024; year++) {
- for (int qrtr = 1; qrtr <= 4; qrtr++) {
- Quarter quarter = Quarter.of(qrtr);
- YearQuarter yearQuarter = YearQuarter.of(year, quarter);
-
- LocalDate expectedStartDate = quarter.firstMonthDay().atYear(year);
- log.info("{} - expectedStartDate: {}", yearQuarter, expectedStartDate);
- LocalDate expectedEndDate = quarter.lastMonthDay().atYear(year);
- log.info("{} - expectedEndDate: {}", yearQuarter, expectedEndDate);
-
- assertEquals(expectedStartDate, yearQuarter.firstDate());
- assertEquals(expectedEndDate, yearQuarter.lastDate());
+ void of_InvalidYearAndInvalidQuarterValue_DateTimeException() {
+ final int[] years = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 };
+ final int[] quarters = { -1, 0, 5, 108 };
+ for (int year : years) {
+ final int yearValue = year;
+ for (int quarter : quarters) {
+ final int quarterValue = quarter;
+ assertThrows(DateTimeException.class,
+ () -> YearQuarter.of(yearValue, quarterValue));
}
}
}
+ // ================================
+ // #endregion - of(int year, int quarter)
+ // ================================
+
+ // ================================
+ // #region - of(int year, Quarter quarter)
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = { 1, 2, 3, 4 })
+ void of_ValidYearAndQuarter_CreatesYearQuarter(int quarterValue) {
+ {
+ int year = 2024;
+ Quarter quarter = Quarter.of(quarterValue);
+ YearQuarter yearQuarter = YearQuarter.of(year, quarter);
+ log.info("{}", yearQuarter);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(quarter, yearQuarter.getQuarter());
+ assertEquals(quarterValue, yearQuarter.getQuarterValue());
+ }
+ {
+ int year = Year.MIN_VALUE;
+ Quarter quarter = Quarter.of(quarterValue);
+ YearQuarter yearQuarter = YearQuarter.of(year, quarter);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(quarter, yearQuarter.getQuarter());
+ assertEquals(quarterValue, yearQuarter.getQuarterValue());
+ }
+ {
+ int year = Year.MAX_VALUE;
+ Quarter quarter = Quarter.of(quarterValue);
+ YearQuarter yearQuarter = YearQuarter.of(year, quarter);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(quarter, yearQuarter.getQuarter());
+ assertEquals(quarterValue, yearQuarter.getQuarterValue());
+ }
+ }
+
+ @Test
+ void of_ValidYearAndNullQuarter_NullPointerException() {
+ int year = 2024;
+ assertThrows(NullPointerException.class, () -> {
+ YearQuarter.of(year, null);
+ });
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 })
+ void of_InvalidYearAndValidQuarter_DateTimeException(int year) {
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, Quarter.Q1);
+ });
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, Quarter.Q2);
+ });
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, Quarter.Q3);
+ });
+ assertThrows(DateTimeException.class, () -> {
+ YearQuarter.of(year, Quarter.Q4);
+ });
+ }
+
+ @Test
+ void of_InvalidYearAndNullQuarter_DateTimeException() {
+ final int[] years = { Year.MIN_VALUE - 1, Year.MAX_VALUE + 1 };
+ for (int year : years) {
+ final int yearValue = year;
+ assertThrows(DateTimeException.class,
+ () -> YearQuarter.of(yearValue, null));
+
+ }
+ }
+
+ // ================================
+ // #endregion - of(int year, Quarter quarter)
+ // ================================
+
+ // ================================
+ // #region - of(LocalDate date)
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_ValidLocalDate_CreatesYearQuarter_Q1(int year) {
+ {
+ LocalDate date = YearMonth.of(year, 1).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 1).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 2).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 2).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 3).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 3).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ }
+
@ParameterizedTest
@ValueSource(ints = {
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
- 2020, 2021, 2022, 2023, 2024
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
})
- void testFirstYearMonth(int year) {
+ void of_ValidLocalDate_CreatesYearQuarter_Q2(int year) {
+ {
+ LocalDate date = YearMonth.of(year, 4).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 4).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 5).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 5).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 6).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 6).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_ValidLocalDate_CreatesYearQuarter_Q3(int year) {
+ {
+ LocalDate date = YearMonth.of(year, 7).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 7).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 8).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 8).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 9).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 9).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_ValidLocalDate_CreatesYearQuarter_Q4(int year) {
+ {
+ LocalDate date = YearMonth.of(year, 10).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 10).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 11).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 11).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 12).atDay(1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ {
+ LocalDate date = YearMonth.of(year, 12).atEndOfMonth();
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ }
+
+ @Test
+ void of_NullLocalDate_NullPointerException() {
+ LocalDate date = null;
+ assertThrows(NullPointerException.class, () -> {
+ YearQuarter.of(date);
+ });
+ }
+
+ // ================================
+ // #endregion - of(LocalDate date)
+ // ================================
+
+ // ================================
+ // #region - of(Date date)
+ // ================================
+
+ @SuppressWarnings("deprecation")
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ 1,
+ 999999,
+ })
+ void of_ValidDate_CreatesYearQuarter(int year) {
+ {
+ Date date = new Date(year - 1900, 1 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ Date date = new Date(year - 1900, 3 - 1, 31, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ Date date = new Date(year - 1900, 4 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ Date date = new Date(year - 1900, 6 - 1, 30, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ Date date = new Date(year - 1900, 7 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ Date date = new Date(year - 1900, 9 - 1, 30, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ Date date = new Date(year - 1900, 10 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ {
+ Date date = new Date(year - 1900, 12 - 1, 31, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ }
+
+ @Test
+ void of_NullDate_NullPointerException() {
+ Date date = null;
+ assertThrows(NullPointerException.class, () -> {
+ YearQuarter.of(date);
+ });
+ }
+
+ // ================================
+ // #endregion - of(Date date)
+ // ================================
+
+ // ================================
+ // #region - of(Calendar date)
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ 1,
+ 999999,
+ })
+ void of_ValidCalendar_CreatesYearQuarter(int year) {
+ Calendar date = Calendar.getInstance();
+ {
+ date.set(year, 1 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ date.set(year, 3 - 1, 31, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(1, yq.getQuarterValue());
+ assertSame(Quarter.Q1, yq.getQuarter());
+ }
+ {
+ date.set(year, 4 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ date.set(year, 6 - 1, 30, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(2, yq.getQuarterValue());
+ assertSame(Quarter.Q2, yq.getQuarter());
+ }
+ {
+ date.set(year, 7 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ date.set(year, 9 - 1, 30, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(3, yq.getQuarterValue());
+ assertSame(Quarter.Q3, yq.getQuarter());
+ }
+ {
+ date.set(year, 10 - 1, 1);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ {
+ date.set(year, 12 - 1, 31, 23, 59, 59);
+ YearQuarter yq = YearQuarter.of(date);
+ assertEquals(year, yq.getYear());
+ assertEquals(4, yq.getQuarterValue());
+ assertSame(Quarter.Q4, yq.getQuarter());
+ }
+ }
+
+ @Test
+ void of_NullCalendar_NullPointerException() {
+ Calendar date = null;
+ assertThrows(NullPointerException.class, () -> {
+ YearQuarter.of(date);
+ });
+ }
+
+ // ================================
+ // #endregion - of(Calendar date)
+ // ================================
+
+ // ================================
+ // #region - of(YearMonth yearMonth)
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_ValidYearMonth_CreatesYearMnoth_Q1(int year) {
+ {
+ YearMonth yearMonth = YearMonth.of(year, 1);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(1, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q1, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 2);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(1, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q1, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 3);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(1, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q1, yearQuarter.getQuarter());
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_ValidYearMonth_CreatesYearMnoth_Q2(int year) {
+ {
+ YearMonth yearMonth = YearMonth.of(year, 4);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(2, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q2, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 5);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(2, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q2, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 6);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(2, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q2, yearQuarter.getQuarter());
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_ValidYearMonth_CreatesYearMnoth_Q3(int year) {
+ {
+ YearMonth yearMonth = YearMonth.of(year, 7);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(3, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q3, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 8);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(3, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q3, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 9);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(3, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q3, yearQuarter.getQuarter());
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_ValidYearMonth_CreatesYearMnoth_Q4(int year) {
+ {
+ YearMonth yearMonth = YearMonth.of(year, 10);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(4, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q4, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 11);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(4, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q4, yearQuarter.getQuarter());
+ }
+ {
+ YearMonth yearMonth = YearMonth.of(year, 12);
+ YearQuarter yearQuarter = YearQuarter.of(yearMonth);
+ assertEquals(year, yearQuarter.getYear());
+ assertEquals(4, yearQuarter.getQuarterValue());
+ assertSame(Quarter.Q4, yearQuarter.getQuarter());
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {
+ 2023, // 非闰年
+ 2024, // 闰年
+ Year.MIN_VALUE,
+ Year.MAX_VALUE,
+ })
+ void of_NullYearMonth_CreatesYearMnoth_Q4(int year) {
+ YearMonth yearMonth = null;
+ assertThrows(NullPointerException.class,
+ () -> YearQuarter.of(yearMonth));
+ }
+
+ // ================================
+ // #endregion - of(YearMonth yearMonth)
+ // ================================
+
+ // ================================
+ // #region - firstDate & lastDate
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = { 1949, 1990, 2000, 2008, 2023, 2024, Year.MIN_VALUE, Year.MAX_VALUE })
+ void test_getFirstDate_And_getLastDate(int year) {
+ {
+ final int quarterValue = 1;
+ YearQuarter yearQuarter = YearQuarter.of(year, quarterValue);
+
+ LocalDate expectedFirstDate = LocalDate.of(year, 1, 1);
+ log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate);
+ LocalDate expectedLastDate = LocalDate.of(year, 3, 31);
+ log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate);
+
+ assertEquals(expectedFirstDate, yearQuarter.firstDate());
+ assertEquals(expectedLastDate, yearQuarter.lastDate());
+ }
+ {
+ final int quarterValue = 2;
+ YearQuarter yearQuarter = YearQuarter.of(year, quarterValue);
+
+ LocalDate expectedFirstDate = LocalDate.of(year, 4, 1);
+ log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate);
+ LocalDate expectedLastDate = LocalDate.of(year, 6, 30);
+ log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate);
+
+ assertEquals(expectedFirstDate, yearQuarter.firstDate());
+ assertEquals(expectedLastDate, yearQuarter.lastDate());
+ }
+ {
+ final int quarterValue = 3;
+ YearQuarter yearQuarter = YearQuarter.of(year, quarterValue);
+
+ LocalDate expectedFirstDate = LocalDate.of(year, 7, 1);
+ log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate);
+ LocalDate expectedLastDate = LocalDate.of(year, 9, 30);
+ log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate);
+
+ assertEquals(expectedFirstDate, yearQuarter.firstDate());
+ assertEquals(expectedLastDate, yearQuarter.lastDate());
+ }
+ {
+ final int quarterValue = 4;
+ YearQuarter yearQuarter = YearQuarter.of(year, quarterValue);
+
+ LocalDate expectedFirstDate = LocalDate.of(year, 10, 1);
+ log.info("{} - expectedFirstDate: {}", yearQuarter, expectedFirstDate);
+ LocalDate expectedLastDate = LocalDate.of(year, 12, 31);
+ log.info("{} - expectedLastDate: {}", yearQuarter, expectedLastDate);
+
+ assertEquals(expectedFirstDate, yearQuarter.firstDate());
+ assertEquals(expectedLastDate, yearQuarter.lastDate());
+ }
+ }
+
+ // ================================
+ // #endregion - firstDate & lastDate
+ // ================================
+
+ // ================================
+ // #region - firstYearMonth & lastYearMonth
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = { 1949, 1990, 2000, 2008, 2023, 2024, Year.MIN_VALUE, Year.MAX_VALUE })
+ void test_firstYearMonth_And_lastYearMonth(int year) {
YearQuarter yq;
yq = YearQuarter.of(year, Quarter.Q1);
@@ -102,6 +811,50 @@ public class YearQuarterTests {
assertEquals(YearMonth.of(year, 12), yq.lastYearMonth());
}
+ // ================================
+ // #endregion - firstYearMonth & lastYearMonth
+ // ================================
+
+ // ================================
+ // #region - firstMonth & lastMonth
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = { 1949, 1990, 2000, 2008, 2023, 2024, Year.MIN_VALUE, Year.MAX_VALUE })
+ void testFirstMonthAndLastMonth(int year) {
+ YearQuarter q1 = YearQuarter.of(year, 1);
+ assertEquals(1, q1.firstMonthValue());
+ assertEquals(Month.JANUARY, q1.firstMonth());
+ assertEquals(3, q1.lastMonthValue());
+ assertEquals(Month.MARCH, q1.lastMonth());
+
+ YearQuarter q2 = YearQuarter.of(year, 2);
+ assertEquals(4, q2.firstMonthValue());
+ assertEquals(Month.APRIL, q2.firstMonth());
+ assertEquals(6, q2.lastMonthValue());
+ assertEquals(Month.JUNE, q2.lastMonth());
+
+ YearQuarter q3 = YearQuarter.of(year, 3);
+ assertEquals(7, q3.firstMonthValue());
+ assertEquals(Month.JULY, q3.firstMonth());
+ assertEquals(9, q3.lastMonthValue());
+ assertEquals(Month.SEPTEMBER, q3.lastMonth());
+
+ YearQuarter q4 = YearQuarter.of(year, 4);
+ assertEquals(10, q4.firstMonthValue());
+ assertEquals(Month.OCTOBER, q4.firstMonth());
+ assertEquals(12, q4.lastMonthValue());
+ assertEquals(Month.DECEMBER, q4.lastMonth());
+ }
+
+ // ================================
+ // #endregion - firstMonth & lastMonth
+ // ================================
+
+ // ================================
+ // #region - compareTo
+ // ================================
+
@Test
void testCompareTo() {
int year1;
@@ -124,23 +877,20 @@ public class YearQuarterTests {
// 同年同季度
assertEquals(yearQuarter1, yearQuarter2);
assertEquals(0, yearQuarter1.compareTo(yearQuarter2));
- }
- else if (quarter1 < quarter2) {
+ } else if (quarter1 < quarter2) {
assertNotEquals(yearQuarter1, yearQuarter2);
assertTrue(yearQuarter1.isBefore(yearQuarter2));
assertFalse(yearQuarter1.isAfter(yearQuarter2));
assertFalse(yearQuarter2.isBefore(yearQuarter1));
assertTrue(yearQuarter2.isAfter(yearQuarter1));
- }
- else if (quarter1 > quarter2) {
+ } else if (quarter1 > quarter2) {
assertNotEquals(yearQuarter1, yearQuarter2);
assertFalse(yearQuarter1.isBefore(yearQuarter2));
assertTrue(yearQuarter1.isAfter(yearQuarter2));
assertTrue(yearQuarter2.isBefore(yearQuarter1));
assertFalse(yearQuarter2.isAfter(yearQuarter1));
}
- }
- else {
+ } else {
// 不同年
assertEquals(year1 - year2, yearQuarter1.compareTo(yearQuarter2));
assertNotEquals(0, yearQuarter1.compareTo(yearQuarter2));
@@ -150,8 +900,7 @@ public class YearQuarterTests {
assertFalse(yearQuarter1.isAfter(yearQuarter2));
assertFalse(yearQuarter2.isBefore(yearQuarter1));
assertTrue(yearQuarter2.isAfter(yearQuarter1));
- }
- else if (year1 > year2) {
+ } else if (year1 > year2) {
assertNotEquals(yearQuarter1, yearQuarter2);
assertFalse(yearQuarter1.isBefore(yearQuarter2));
assertTrue(yearQuarter1.isAfter(yearQuarter2));
@@ -162,4 +911,281 @@ public class YearQuarterTests {
}
}
}
+
+ // ================================
+ // #endregion - compareTo
+ // ================================
+
+ @ParameterizedTest
+ @ValueSource(ints = { Year.MIN_VALUE + 25, Year.MAX_VALUE - 25, -1, 0, 1, 1949, 1990, 2000, 2008, 2023, 2024 })
+ void testPlusQuartersAndMinusQuarters(int year) {
+ for (int quarter = 1; quarter <= 4; quarter++) {
+ YearQuarter yq1 = YearQuarter.of(year, quarter);
+ for (int quartersToAdd = -100; quartersToAdd <= 100; quartersToAdd++) {
+ YearQuarter plus = yq1.plusQuarters(quartersToAdd);
+ YearQuarter minus = yq1.minusQuarters(-quartersToAdd);
+ assertEquals(plus, minus);
+
+ // offset: 表示自 公元 0000年以来,经历了多少季度。所以 0 表示 -0001,Q4; 1 表示 0000 Q1
+ long offset = (year * 4L + quarter) + quartersToAdd;
+ if (offset > 0) {
+ assertEquals((offset - 1) / 4, plus.getYear());
+ assertEquals(((offset - 1) % 4) + 1, plus.getQuarterValue());
+ } else {
+ assertEquals((offset / 4 - 1), plus.getYear());
+ assertEquals((4 + offset % 4), plus.getQuarterValue());
+ }
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { Year.MIN_VALUE + 1, Year.MAX_VALUE - 1, -1, 0, 1, 1900, 1990, 2000, 2023, 2024 })
+ void test_nextQuarter_And_lastQuarter(int year) {
+ int quarter;
+
+ YearQuarter yq;
+ YearQuarter next;
+ YearQuarter last;
+
+ quarter = 1;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextQuarter();
+ assertEquals(year, next.getYear());
+ assertEquals(2, next.getQuarterValue());
+ last = yq.lastQuarter();
+ assertEquals(year - 1, last.getYear());
+ assertEquals(4, last.getQuarterValue());
+
+ quarter = 2;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextQuarter();
+ assertEquals(year, next.getYear());
+ assertEquals(3, next.getQuarterValue());
+ last = yq.lastQuarter();
+ assertEquals(year, last.getYear());
+ assertEquals(1, last.getQuarterValue());
+
+ quarter = 3;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextQuarter();
+ assertEquals(year, next.getYear());
+ assertEquals(4, next.getQuarterValue());
+ last = yq.lastQuarter();
+ assertEquals(year, last.getYear());
+ assertEquals(2, last.getQuarterValue());
+
+ quarter = 4;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextQuarter();
+ assertEquals(year + 1, next.getYear());
+ assertEquals(1, next.getQuarterValue());
+ last = yq.lastQuarter();
+ assertEquals(year, last.getYear());
+ assertEquals(3, last.getQuarterValue());
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { Year.MIN_VALUE + 100, Year.MAX_VALUE - 100, -1, 0, 1, 1949, 1990, 2000, 2008, 2023, 2024 })
+ void test_PlusYearsAndMinusYears(int year) {
+ for (int yearToAdd = -100; yearToAdd <= 100; yearToAdd++) {
+ YearQuarter q1 = YearQuarter.of(year, Quarter.Q1);
+ YearQuarter plus = q1.plusYears(yearToAdd);
+ assertEquals(year + yearToAdd, plus.getYear());
+ assertEquals(Quarter.Q1, plus.getQuarter());
+ YearQuarter minus = q1.minusYears(yearToAdd);
+ assertEquals(Quarter.Q1, minus.getQuarter());
+ assertEquals(year - yearToAdd, minus.getYear());
+
+ assertEquals(q1.plusYears(yearToAdd), q1.minusYears(-yearToAdd));
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { Year.MIN_VALUE + 1, Year.MAX_VALUE - 1, -1, 0, 1, 1900, 1990, 2000, 2023, 2024 })
+ void test_nextYear_And_lastYear(int year) {
+ int quarter;
+
+ YearQuarter yq;
+ YearQuarter next;
+ YearQuarter last;
+
+ quarter = 1;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextYear();
+ assertSame(Quarter.Q1, yq.getQuarter());
+ assertEquals(year + 1, next.getYear());
+ assertSame(Quarter.Q1, next.getQuarter());
+ last = yq.lastYear();
+ assertEquals(year - 1, last.getYear());
+ assertSame(Quarter.Q1, last.getQuarter());
+
+ quarter = 2;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextYear();
+ assertSame(Quarter.Q2, yq.getQuarter());
+ assertEquals(year + 1, next.getYear());
+ assertSame(Quarter.Q2, next.getQuarter());
+ last = yq.lastYear();
+ assertEquals(year - 1, last.getYear());
+ assertSame(Quarter.Q2, last.getQuarter());
+
+ quarter = 3;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextYear();
+ assertSame(Quarter.Q3, yq.getQuarter());
+ assertEquals(year + 1, next.getYear());
+ assertSame(Quarter.Q3, next.getQuarter());
+ last = yq.lastYear();
+ assertEquals(year - 1, last.getYear());
+ assertSame(Quarter.Q3, last.getQuarter());
+
+ quarter = 4;
+ yq = YearQuarter.of(year, quarter);
+ next = yq.nextYear();
+ assertSame(Quarter.Q4, yq.getQuarter());
+ assertEquals(year + 1, next.getYear());
+ assertSame(Quarter.Q4, next.getQuarter());
+ last = yq.lastYear();
+ assertEquals(year - 1, last.getYear());
+ assertSame(Quarter.Q4, last.getQuarter());
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE })
+ void test_compareTo_sameYear(int year) {
+ YearQuarter yq1 = YearQuarter.of(year, 1);
+ YearQuarter yq2 = YearQuarter.of(year, 2);
+ YearQuarter yq3 = YearQuarter.of(year, 3);
+ YearQuarter yq4 = YearQuarter.of(year, 4);
+
+ assertTrue(yq1.equals(YearQuarter.of(year, Quarter.Q1))); // NOSONAR
+ assertTrue(yq1.compareTo(yq1) == 0); // NOSONAR
+ assertTrue(yq1.compareTo(yq2) < 0);
+ assertTrue(yq1.compareTo(yq3) < 0);
+ assertTrue(yq1.compareTo(yq4) < 0);
+
+ assertTrue(yq2.equals(YearQuarter.of(year, Quarter.Q2))); // NOSONAR
+ assertTrue(yq2.compareTo(yq1) > 0);
+ assertTrue(yq2.compareTo(yq2) == 0); // NOSONAR
+ assertTrue(yq2.compareTo(yq3) < 0);
+ assertTrue(yq2.compareTo(yq4) < 0);
+
+ assertTrue(yq3.equals(YearQuarter.of(year, Quarter.Q3))); // NOSONAR
+ assertTrue(yq3.compareTo(yq1) > 0);
+ assertTrue(yq3.compareTo(yq2) > 0);
+ assertTrue(yq3.compareTo(yq3) == 0); // NOSONAR
+ assertTrue(yq3.compareTo(yq4) < 0);
+
+ assertTrue(yq4.equals(YearQuarter.of(year, Quarter.Q4))); // NOSONAR
+ assertTrue(yq4.compareTo(yq1) > 0);
+ assertTrue(yq4.compareTo(yq2) > 0);
+ assertTrue(yq4.compareTo(yq3) > 0);
+ assertTrue(yq4.compareTo(yq4) == 0); // NOSONAR
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE })
+ void test_isBefore_sameYear(int year) {
+ YearQuarter yq1 = YearQuarter.of(year, 1);
+ YearQuarter yq2 = YearQuarter.of(year, 2);
+ YearQuarter yq3 = YearQuarter.of(year, 3);
+ YearQuarter yq4 = YearQuarter.of(year, 4);
+
+ assertFalse(yq1.isBefore(YearQuarter.of(year, Quarter.Q1)));
+ assertTrue(yq1.isBefore(yq2));
+ assertTrue(yq1.isBefore(yq3));
+ assertTrue(yq1.isBefore(yq4));
+
+ assertFalse(yq2.isBefore(yq1));
+ assertFalse(yq2.isBefore(YearQuarter.of(year, Quarter.Q2)));
+ assertTrue(yq2.isBefore(yq3));
+ assertTrue(yq2.isBefore(yq4));
+
+ assertFalse(yq3.isBefore(yq1));
+ assertFalse(yq3.isBefore(yq2));
+ assertFalse(yq3.isBefore(YearQuarter.of(year, Quarter.Q3)));
+ assertTrue(yq3.isBefore(yq4));
+
+ assertFalse(yq4.isBefore(yq1));
+ assertFalse(yq4.isBefore(yq2));
+ assertFalse(yq4.isBefore(yq3));
+ assertFalse(yq4.isBefore(YearQuarter.of(year, Quarter.Q4)));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE })
+ void test_isAfter_sameYear(int year) {
+ YearQuarter yq1 = YearQuarter.of(year, 1);
+ YearQuarter yq2 = YearQuarter.of(year, 2);
+ YearQuarter yq3 = YearQuarter.of(year, 3);
+ YearQuarter yq4 = YearQuarter.of(year, 4);
+
+ assertFalse(yq1.isAfter(YearQuarter.of(year, Quarter.Q1)));
+ assertFalse(yq1.isAfter(yq2));
+ assertFalse(yq1.isAfter(yq3));
+ assertFalse(yq1.isAfter(yq4));
+
+ assertTrue(yq2.isAfter(yq1));
+ assertFalse(yq2.isAfter(YearQuarter.of(year, Quarter.Q2)));
+ assertFalse(yq2.isAfter(yq3));
+ assertFalse(yq2.isAfter(yq4));
+
+ assertTrue(yq3.isAfter(yq1));
+ assertTrue(yq3.isAfter(yq2));
+ assertFalse(yq3.isAfter(YearQuarter.of(year, Quarter.Q3)));
+ assertFalse(yq3.isAfter(yq4));
+
+ assertTrue(yq4.isAfter(yq1));
+ assertTrue(yq4.isAfter(yq2));
+ assertTrue(yq4.isAfter(yq3));
+ assertFalse(yq4.isAfter(YearQuarter.of(year, Quarter.Q4)));
+ }
+
+ @Test
+ void test_compareTo_null() {
+ YearQuarter yq = YearQuarter.of(2024, 4);
+ assertThrows(NullPointerException.class,
+ () -> yq.compareTo(null));
+ assertThrows(NullPointerException.class,
+ () -> yq.isBefore(null));
+ assertThrows(NullPointerException.class,
+ () -> yq.isAfter(null));
+ assertNotEquals(null, yq);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE - 1, Year.MIN_VALUE + 1 })
+ void test_compareTo_differentYear(int year) {
+ for (int quarter1 = 1; quarter1 <= 4; quarter1++) {
+ YearQuarter yq = YearQuarter.of(year, quarter1);
+ for (int quarter2 = 1; quarter2 <= 4; quarter2++) {
+ // gt
+ assertTrue(yq.compareTo(YearQuarter.of(year + 1, quarter2)) < 0);
+ assertTrue(yq.isBefore(YearQuarter.of(year + 1, quarter2)));
+ assertTrue(YearQuarter.of(year + 1, quarter2).compareTo(yq) > 0);
+ assertTrue(YearQuarter.of(year + 1, quarter2).isAfter(yq));
+ // lt
+ assertTrue(yq.compareTo(YearQuarter.of(year - 1, quarter2)) > 0);
+ assertTrue(yq.isAfter(YearQuarter.of(year - 1, quarter2)));
+ assertTrue(YearQuarter.of(year - 1, quarter2).compareTo(yq) < 0);
+ assertTrue(YearQuarter.of(year - 1, quarter2).isBefore(yq));
+ }
+ }
+ }
+
+ @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));
+
+ }
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java
index 6b23fb2..32423a1 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/ArrayToolsTests.java
@@ -16,21 +16,1075 @@
package xyz.zhouxy.plusone.commons.util;
+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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+
import org.junit.jupiter.api.Test;
-@SuppressWarnings("all")
+@SuppressWarnings("null")
public class ArrayToolsTests {
+
+ static final String[] NULL_STRING_ARRAY = null;
+ static final Integer[] NULL_INTEGER_ARRAY = null;
+ static final char[] NULL_CHAR_ARRAY = null;
+ static final byte[] NULL_BYTE_ARRAY = null;
+ static final short[] NULL_SHORT_ARRAY = null;
+ static final int[] NULL_INT_ARRAY = null;
+ static final long[] NULL_LONG_ARRAY = null;
+ static final float[] NULL_FLOAT_ARRAY = null;
+ static final double[] NULL_DOUBLE_ARRAY = null;
+
+ static final String[] EMPTY_STRING_ARRAY = {};
+ static final Integer[] EMPTY_INTEGER_ARRAY = {};
+ static final char[] EMPTY_CHAR_ARRAY = {};
+ static final byte[] EMPTY_BYTE_ARRAY = {};
+ static final short[] EMPTY_SHORT_ARRAY = {};
+ static final int[] EMPTY_INT_ARRAY = {};
+ static final long[] EMPTY_LONG_ARRAY = {};
+ static final float[] EMPTY_FLOAT_ARRAY = {};
+ static final double[] EMPTY_DOUBLE_ARRAY = {};
+
+ // ================================
+ // #region - isNullOrEmpty
+ // ================================
+
@Test
- void testIsAllNotNull() {
- assertTrue(ArrayTools.isAllElementsNotNull(new Object[] { 1L, 2, 3.0, "Java" }));
- assertFalse(ArrayTools.isAllElementsNotNull(new Object[] { 1L, 2, 3.0, "Java", null }));
- assertFalse(ArrayTools.isAllElementsNotNull(new Object[] { null, 1L, 2, 3.0, "Java" }));
- assertTrue(ArrayTools.isAllElementsNotNull(new Object[] {}));
- assertThrows(IllegalArgumentException.class,
- () -> ArrayTools.isAllElementsNotNull(null));
+ void isNullOrEmpty_NullArray_ReturnsTrue() {
+ assertAll(
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_STRING_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_INTEGER_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_CHAR_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_BYTE_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_SHORT_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_INT_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_LONG_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_FLOAT_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(NULL_DOUBLE_ARRAY)));
}
+
+ @Test
+ void isNullOrEmpty_EmptyArray_ReturnsTrue() {
+ assertAll(
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_STRING_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_INTEGER_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_CHAR_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_BYTE_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_SHORT_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_INT_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_LONG_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_FLOAT_ARRAY)),
+ () -> assertTrue(ArrayTools.isNullOrEmpty(EMPTY_DOUBLE_ARRAY)));
+ }
+
+ @Test
+ void isNullOrEmpty_NonEmptyArray_ReturnsFalse() {
+ assertAll(
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new String[] { "a" })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new Integer[] { 1 })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new char[] { 'a' })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new byte[] { 1 })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new short[] { 1 })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new int[] { 1 })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new long[] { 1 })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new float[] { 1 })),
+ () -> assertFalse(ArrayTools.isNullOrEmpty(new double[] { 1 })));
+ }
+
+ // ================================
+ // #endregion - isNullOrEmpty
+ // ================================
+
+ // ================================
+ // #region - isNotEmpty
+ // ================================
+
+ @Test
+ void isNotEmpty_NullArray_ReturnsFalse() {
+ assertAll(
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_STRING_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_INTEGER_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_CHAR_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_BYTE_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_SHORT_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_INT_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_LONG_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_FLOAT_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(NULL_DOUBLE_ARRAY)));
+ }
+
+ @Test
+ void isNotEmpty_EmptyArray_ReturnsFalse() {
+ assertAll(
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_STRING_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_INTEGER_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_CHAR_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_BYTE_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_SHORT_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_INT_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_LONG_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_FLOAT_ARRAY)),
+ () -> assertFalse(ArrayTools.isNotEmpty(EMPTY_DOUBLE_ARRAY)));
+ }
+
+ @Test
+ void isNotEmpty_NonEmptyArray_ReturnsTrue() {
+ assertAll(
+ () -> assertTrue(ArrayTools.isNotEmpty(new String[] { "a" })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new Integer[] { 1 })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new char[] { 'a' })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new byte[] { 1 })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new short[] { 1 })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new int[] { 1 })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new long[] { 1 })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new float[] { 1 })),
+ () -> assertTrue(ArrayTools.isNotEmpty(new double[] { 1 })));
+ }
+
+ // ================================
+ // #endregion - isNotEmpty
+ // ================================
+
+ // ================================
+ // #region - isAllElementsNotNull
+ // ================================
+
+ @Test
+ void isAllElementsNotNull_NullArray_ThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.isAllElementsNotNull(NULL_STRING_ARRAY));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.isAllElementsNotNull(NULL_INTEGER_ARRAY));
+ }
+
+ @Test
+ void isAllElementsNotNull_EmptyArray_ReturnsTrue() {
+ assertTrue(ArrayTools.isAllElementsNotNull(EMPTY_STRING_ARRAY));
+ assertTrue(ArrayTools.isAllElementsNotNull(EMPTY_INTEGER_ARRAY));
+ }
+
+ @Test
+ void isAllElementsNotNull_ArrayWithNullElement_ReturnsFalse() {
+ assertFalse(ArrayTools.isAllElementsNotNull(new String[] { "a", null }));
+ assertFalse(ArrayTools.isAllElementsNotNull(new Integer[] { 1, null }));
+ }
+
+ @Test
+ void isAllElementsNotNull_ArrayWithoutNullElements_ReturnsTrue() {
+ assertTrue(ArrayTools.isAllElementsNotNull(new String[] { "a", "b" }));
+ assertTrue(ArrayTools.isAllElementsNotNull(new Integer[] { 1, 2 }));
+ }
+
+ // ================================
+ // #endregion - isAllElementsNotNull
+ // ================================
+
+ // ================================
+ // #region - concat
+ // ================================
+
+ static final List charArrays;
+ static final List byteArrays;
+ static final List shortArrays;
+ static final List intArrays;
+ static final List longArrays;
+ static final List floatArrays;
+ static final List doubleArrays;
+
+ static {
+ charArrays = new ArrayList<>();
+ charArrays.add(null);
+ charArrays.add(new char[0]);
+ charArrays.add(new char[] { 'a', 'b' });
+ charArrays.add(new char[] { 'c', 'd', 'e' });
+
+ byteArrays = new ArrayList<>();
+ byteArrays.add(null);
+ byteArrays.add(new byte[0]);
+ byteArrays.add(new byte[] { 1, 2 });
+ byteArrays.add(new byte[] { 3, 4, 5 });
+
+ shortArrays = new ArrayList<>();
+ shortArrays.add(null);
+ shortArrays.add(new short[0]);
+ shortArrays.add(new short[] { 10, 20 });
+ shortArrays.add(new short[] { 30, 40, 50 });
+
+ intArrays = new ArrayList<>();
+ intArrays.add(null);
+ intArrays.add(new int[0]);
+ intArrays.add(new int[] { 100, 200 });
+ intArrays.add(new int[] { 300, 400, 500 });
+
+ longArrays = new ArrayList<>();
+ longArrays.add(null);
+ longArrays.add(new long[0]);
+ longArrays.add(new long[] { 1000L, 2000L });
+ longArrays.add(new long[] { 3000L, 4000L, 5000L });
+
+ floatArrays = new ArrayList<>();
+ floatArrays.add(null);
+ floatArrays.add(new float[0]);
+ floatArrays.add(new float[] { 1000.1f, 2000.2f });
+ floatArrays.add(new float[] { 3000.3f, 4000.4f, 5000.5f });
+
+ doubleArrays = new ArrayList<>();
+ doubleArrays.add(null);
+ doubleArrays.add(new double[0]);
+ doubleArrays.add(new double[] { 1000.1d, 2000.2d });
+ doubleArrays.add(new double[] { 3000.3d, 4000.4d, 5000.5d });
+ }
+
+ @Test
+ void concat_NullOrEmptyCollection_ReturnsEmptyArray() {
+ assertEquals(0, ArrayTools.concatCharArray(null).length);
+ assertEquals(0, ArrayTools.concatCharArray(Collections.emptyList()).length);
+
+ assertEquals(0, ArrayTools.concatByteArray(null).length);
+ assertEquals(0, ArrayTools.concatByteArray(Collections.emptyList()).length);
+
+ assertEquals(0, ArrayTools.concatShortArray(null).length);
+ assertEquals(0, ArrayTools.concatShortArray(Collections.emptyList()).length);
+
+ assertEquals(0, ArrayTools.concatIntArray(null).length);
+ assertEquals(0, ArrayTools.concatIntArray(Collections.emptyList()).length);
+
+ assertEquals(0, ArrayTools.concatLongArray(null).length);
+ assertEquals(0, ArrayTools.concatLongArray(Collections.emptyList()).length);
+
+ assertEquals(0, ArrayTools.concatFloatArray(null).length);
+ assertEquals(0, ArrayTools.concatFloatArray(Collections.emptyList()).length);
+
+ assertEquals(0, ArrayTools.concatDoubleArray(null).length);
+ assertEquals(0, ArrayTools.concatDoubleArray(Collections.emptyList()).length);
+ }
+
+ @Test
+ void concat_ValidCollection_ReturnsConcatenatedArray() {
+ assertArrayEquals(new char[] { 'a', 'b', 'c', 'd', 'e' },
+ ArrayTools.concatCharArray(charArrays));
+
+ assertArrayEquals(new byte[] { 1, 2, 3, 4, 5 },
+ ArrayTools.concatByteArray(byteArrays));
+
+ assertArrayEquals(new short[] { 10, 20, 30, 40, 50 },
+ ArrayTools.concatShortArray(shortArrays));
+
+ assertArrayEquals(new int[] { 100, 200, 300, 400, 500 },
+ ArrayTools.concatIntArray(intArrays));
+
+ assertArrayEquals(new long[] { 1000L, 2000L, 3000L, 4000L, 5000L },
+ ArrayTools.concatLongArray(longArrays));
+
+ assertArrayEquals(new float[] { 1000.1f, 2000.2f, 3000.3f, 4000.4f, 5000.5f },
+ ArrayTools.concatFloatArray(floatArrays));
+
+ assertArrayEquals(new double[] { 1000.1d, 2000.2d, 3000.3d, 4000.4d, 5000.5d },
+ ArrayTools.concatDoubleArray(doubleArrays));
+ }
+
+ @Test
+ void concatToList_NullOrEmptyCollection_ReturnsEmptyList() {
+ assertTrue(ArrayTools.concatToList(null).isEmpty());
+ assertTrue(ArrayTools.concatToList(Collections.emptyList()).isEmpty());
+ }
+
+ @Test
+ void concatToList_ValidCollection_ReturnsConcatenatedList() {
+ Character[] charArray1 = { 'a', 'b' };
+ Character[] charArray2 = { 'c', 'd', 'e' };
+ List expected = Arrays.asList('a', 'b', 'c', 'd', 'e');
+ List result = ArrayTools.concatToList(Arrays.asList(charArray1, charArray2));
+ assertEquals(expected, result);
+ }
+
+ // ================================
+ // #endregion
+ // ================================
+
+ // ================================
+ // #region - repeat
+ // ================================
+
+ @Test
+ void repeat_NullArray_ThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_CHAR_ARRAY, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_BYTE_ARRAY, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_SHORT_ARRAY, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_INT_ARRAY, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_LONG_ARRAY, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_FLOAT_ARRAY, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_DOUBLE_ARRAY, 2));
+
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_CHAR_ARRAY, 2, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_BYTE_ARRAY, 2, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_SHORT_ARRAY, 2, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_INT_ARRAY, 2, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_LONG_ARRAY, 2, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_FLOAT_ARRAY, 2, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(NULL_DOUBLE_ARRAY, 2, 2));
+ }
+
+ @Test
+ void repeat_NegativeTimes_ThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new char[]{ 'a' }, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new byte[]{ 1 }, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new short[]{ 1 }, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new int[]{ 1 }, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new long[]{ 1 }, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new float[]{ 1 }, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new double[]{ 1 }, -1));
+
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new char[]{ 'a' }, -1, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new byte[]{ 1 }, -1, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new short[]{ 1 }, -1, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new int[]{ 1 }, -1, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new long[]{ 1 }, -1, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new float[]{ 1 }, -1, 2));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new double[]{ 1 }, -1, 2));
+ }
+
+ @Test
+ void repeat_NegativeMaxLength_ThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new char[]{ 'a' }, 2, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new byte[]{ 1 }, 2, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new short[]{ 1 }, 2, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new int[]{ 1 }, 2, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new long[]{ 1 }, 2, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new float[]{ 1 }, 2, -1));
+ assertThrows(IllegalArgumentException.class, () -> ArrayTools.repeat(new double[]{ 1 }, 2, -1));
+ }
+
+ @Test
+ void repeat_ZeroTimes_ReturnsEmptyArray() {
+ assertEquals(0, ArrayTools.repeat(new char[]{ 'a' }, 0).length);
+ assertEquals(0, ArrayTools.repeat(new byte[]{ 1 }, 0).length);
+ assertEquals(0, ArrayTools.repeat(new short[]{ 1 }, 0).length);
+ assertEquals(0, ArrayTools.repeat(new int[]{ 1 }, 0).length);
+ assertEquals(0, ArrayTools.repeat(new long[]{ 1 }, 0).length);
+ assertEquals(0, ArrayTools.repeat(new float[]{ 1 }, 0).length);
+ assertEquals(0, ArrayTools.repeat(new double[]{ 1 }, 0).length);
+ }
+
+ @Test
+ void repeat_NormalCase_RepeatsArray() {
+ assertArrayEquals(new char[] { 'a', 'b', 'a', 'b', 'a', 'b' }, ArrayTools.repeat(new char[] { 'a', 'b' }, 3));
+ assertArrayEquals(new byte[] { 1, 2, 1, 2, 1, 2 }, ArrayTools.repeat(new byte[] { 1, 2 }, 3));
+ assertArrayEquals(new short[] { 1, 2, 1, 2, 1, 2 }, ArrayTools.repeat(new short[] { 1, 2 }, 3));
+ assertArrayEquals(new int[] { 1, 2, 1, 2, 1, 2 }, ArrayTools.repeat(new int[] { 1, 2 }, 3));
+ assertArrayEquals(new long[] { 1L, 2L, 1L, 2L, 1L, 2L }, ArrayTools.repeat(new long[] { 1L, 2L }, 3));
+ assertArrayEquals(new float[] { 1.1F, 2.2F, 1.1F, 2.2F, 1.1F, 2.2F }, ArrayTools.repeat(new float[] { 1.1F, 2.2F }, 3));
+ assertArrayEquals(new double[] { 1.12, 2.23, 1.12, 2.23, 1.12, 2.23 }, ArrayTools.repeat(new double[] { 1.12, 2.23 }, 3));
+ }
+
+ @Test
+ void repeat_WithMaxLength_TruncatesResult() {
+ assertArrayEquals(new char[] { 'a', 'b', 'a', 'b', 'a' }, ArrayTools.repeat(new char[] { 'a', 'b' }, 3, 5));
+ assertArrayEquals(new byte[] { 1, 2, 1, 2, 1 }, ArrayTools.repeat(new byte[] { 1, 2 }, 3, 5));
+ assertArrayEquals(new short[] { 1, 2, 1, 2, 1 }, ArrayTools.repeat(new short[] { 1, 2 }, 3, 5));
+ assertArrayEquals(new int[] { 1, 2, 1, 2, 1 }, ArrayTools.repeat(new int[] { 1, 2 }, 3, 5));
+ assertArrayEquals(new long[] { 1L, 2L, 1L, 2L, 1L }, ArrayTools.repeat(new long[] { 1L, 2L }, 3, 5));
+ assertArrayEquals(new float[] { 1.1F, 2.2F, 1.1F, 2.2F, 1.1F }, ArrayTools.repeat(new float[] { 1.1F, 2.2F }, 3, 5));
+ assertArrayEquals(new double[] { 1.12, 2.23, 1.12, 2.23, 1.12 }, ArrayTools.repeat(new double[] { 1.12, 2.23 }, 3, 5));
+ }
+
+ // ================================
+ // #endregion
+ // ================================
+
+ // ================================
+ // #region - fill
+ // ================================
+
+ @Test
+ void fill_WithValues() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, new char[] {'a', 'b', 'c'});
+ assertArrayEquals(new char[] { 'a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c', 'a' }, charArray);
+ ArrayTools.fill(charArray, "abcd");
+ assertArrayEquals(new char[] { 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b' }, charArray);
+
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, new byte[] { 1, 2, 3 });
+ assertArrayEquals(new byte[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, byteArray);
+
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, new short[] { 1, 2, 3 });
+ assertArrayEquals(new short[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, shortArray);
+
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, new int[] { 1, 2, 3 });
+ assertArrayEquals(new int[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, intArray);
+
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, new long[] { 1, 2, 3 });
+ assertArrayEquals(new long[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }, longArray);
+
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, new float[] { 1.1F, 2.2F, 3.3F });
+ assertArrayEquals(new float[] { 1.1F, 2.2F, 3.3F, 1.1F, 2.2F, 3.3F, 1.1F, 2.2F, 3.3F, 1.1F }, floatArray);
+
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, new double[] { 1.1, 2.2, 3.3 });
+ assertArrayEquals(new double[] { 1.1, 2.2, 3.3, 1.1, 2.2, 3.3, 1.1, 2.2, 3.3, 1.1 }, doubleArray);
+
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, new String[] { "aa", "bb", "cc" });
+ assertArrayEquals(new String[] { "aa", "bb", "cc", "aa", "bb", "cc", "aa", "bb", "cc", "aa" }, stringArray);
+ }
+
+ @Test
+ void fill_WithEmptyValues() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, EMPTY_CHAR_ARRAY);
+ assertArrayEquals(new char[10], charArray);
+ ArrayTools.fill(charArray, "");
+ assertArrayEquals(new char[10], charArray);
+
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, EMPTY_BYTE_ARRAY);
+ assertArrayEquals(new byte[10], byteArray);
+
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, EMPTY_SHORT_ARRAY);
+ assertArrayEquals(new short[10], shortArray);
+
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, EMPTY_INT_ARRAY);
+ assertArrayEquals(new int[10], intArray);
+
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, EMPTY_LONG_ARRAY);
+ assertArrayEquals(new long[10], longArray);
+
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, EMPTY_FLOAT_ARRAY);
+ assertArrayEquals(new float[10], floatArray);
+
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, EMPTY_DOUBLE_ARRAY);
+ assertArrayEquals(new double[10], doubleArray);
+
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, EMPTY_STRING_ARRAY);
+ assertArrayEquals(new String[10], stringArray);
+ }
+
+ @Test
+ void fill_WithNullValues() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, NULL_CHAR_ARRAY);
+ assertArrayEquals(new char[10], charArray);
+ ArrayTools.fill(charArray, (String) null);
+ assertArrayEquals(new char[10], charArray);
+
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, NULL_BYTE_ARRAY);
+ assertArrayEquals(new byte[10], byteArray);
+
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, NULL_SHORT_ARRAY);
+ assertArrayEquals(new short[10], shortArray);
+
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, NULL_INT_ARRAY);
+ assertArrayEquals(new int[10], intArray);
+
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, NULL_LONG_ARRAY);
+ assertArrayEquals(new long[10], longArray);
+
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, NULL_FLOAT_ARRAY);
+ assertArrayEquals(new float[10], floatArray);
+
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, NULL_DOUBLE_ARRAY);
+ assertArrayEquals(new double[10], doubleArray);
+
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, NULL_STRING_ARRAY);
+ assertArrayEquals(new String[10], stringArray);
+ }
+
+ @Test
+ void fill_WithValuesInRange() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, 2, 8, new char[] { 'x', 'y' });
+ assertArrayEquals(new char[] { '\0', '\0', 'x', 'y', 'x', 'y', 'x', 'y', '\0', '\0' }, charArray);
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, 2, 8, new byte[] { 1, 2 });
+ assertArrayEquals(new byte[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, byteArray);
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, 2, 8, new short[] { 1, 2 });
+ assertArrayEquals(new short[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, shortArray);
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, 2, 8, new int[] { 1, 2 });
+ assertArrayEquals(new int[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, intArray);
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, 2, 8, new long[] { 1, 2 });
+ assertArrayEquals(new long[] { 0, 0, 1, 2, 1, 2, 1, 2, 0, 0 }, longArray);
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, 2, 8, new float[] { 1.1F, 2.2F });
+ assertArrayEquals(new float[] { 0F, 0F, 1.1F, 2.2F, 1.1F, 2.2F, 1.1F, 2.2F, 0F, 0F }, floatArray);
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, 2, 8, new double[] { 0.1, 0.2 });
+ assertArrayEquals(new double[] { 0.0, 0.0, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.0, 0.0 }, doubleArray);
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, 2, 8, new String[] { "Java", "C++" });
+ assertArrayEquals(new String[] { null, null, "Java", "C++", "Java", "C++", "Java", "C++", null, null },
+ stringArray);
+ }
+
+ @Test
+ void fill_WithValues_OutOfRange() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, 10, 20, new char[] { 'x', 'y' });
+ assertArrayEquals(new char[10], charArray);
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, 10, 20, new byte[] { 1, 2 });
+ assertArrayEquals(new byte[10], byteArray);
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, 10, 20, new short[] { 1, 2 });
+ assertArrayEquals(new short[10], shortArray);
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, 10, 20, new int[] { 1, 2 });
+ assertArrayEquals(new int[10], intArray);
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, 10, 20, new long[] { 1, 2 });
+ assertArrayEquals(new long[10], longArray);
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, 10, 20, new float[] { 1.1F, 2.2F });
+ assertArrayEquals(new float[10], floatArray);
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, 10, 20, new double[] { 0.1, 0.2 });
+ assertArrayEquals(new double[10], doubleArray);
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, 10, 20, new String[] { "Java", "C++" });
+ assertArrayEquals(new String[10], stringArray);
+ }
+
+ @Test
+ void fill_WithValues_NegativeRange() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, -5, 5, new char[] {'w'});
+ assertArrayEquals(new char[]{'w', 'w', 'w', 'w', 'w', '\0', '\0', '\0', '\0', '\0'}, charArray);
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, -5, 5, new byte[] {108});
+ assertArrayEquals(new byte[]{108, 108, 108, 108, 108, 0, 0, 0, 0, 0}, byteArray);
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, -5, 5, new short[] {108});
+ assertArrayEquals(new short[]{108, 108, 108, 108, 108, 0, 0, 0, 0, 0}, shortArray);
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, -5, 5, new int[] {108});
+ assertArrayEquals(new int[]{108, 108, 108, 108, 108, 0, 0, 0, 0, 0}, intArray);
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, -5, 5, new long[] {108L});
+ assertArrayEquals(new long[]{108L, 108L, 108L, 108L, 108L, 0L, 0L, 0L, 0L, 0L}, longArray);
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, -5, 5, new float[] {108.01F});
+ assertArrayEquals(new float[]{108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}, floatArray);
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, -5, 5, new double[] {108.01});
+ assertArrayEquals(new double[]{108.01, 108.01, 108.01, 108.01, 108.01, 0.0, 0.0, 0.0, 0.0, 0.0}, doubleArray);
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, -5, 5, new String[] {"test"});
+ assertArrayEquals(new String[]{"test", "test", "test", "test", "test", null, null, null, null, null}, stringArray);
+ }
+
+ @Test
+ void fill_WithValues_ExactRange() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, 0, 10, new char[] {'w'});
+ assertArrayEquals(new char[]{'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w'}, charArray);
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, 0, 10, new byte[] {108});
+ assertArrayEquals(new byte[]{108, 108, 108, 108, 108, 108, 108, 108, 108, 108}, byteArray);
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, 0, 10, new short[] {108});
+ assertArrayEquals(new short[]{108, 108, 108, 108, 108, 108, 108, 108, 108, 108}, shortArray);
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, 0, 10, new int[] {108});
+ assertArrayEquals(new int[]{108, 108, 108, 108, 108, 108, 108, 108, 108, 108}, intArray);
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, 0, 10, new long[] {108L});
+ assertArrayEquals(new long[]{108L, 108L, 108L, 108L, 108L, 108L, 108L, 108L, 108L, 108L}, longArray);
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, 0, 10, new float[] {108.01F});
+ assertArrayEquals(new float[]{108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F, 108.01F}, floatArray);
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, 0, 10, new double[] {108.01});
+ assertArrayEquals(new double[]{108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01, 108.01}, doubleArray);
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, 0, 10, new String[] {"test"});
+ assertArrayEquals(new String[]{"test", "test", "test", "test", "test", "test", "test", "test", "test", "test"}, stringArray);
+ }
+
+ @Test
+ void fill_WithValues_Overlap() {
+ // fill - char
+ char[] charArray = new char[10];
+ ArrayTools.fill(charArray, 0, 5, new char[] {'a', 'b', 'c', 'd', 'e', 'f'});
+ assertArrayEquals(new char[]{'a', 'b', 'c', 'd', 'e', '\0', '\0', '\0', '\0', '\0'}, charArray);
+ // fill - byte
+ byte[] byteArray = new byte[10];
+ ArrayTools.fill(byteArray, 0, 5, new byte[] {1, 2, 3, 4, 5, 6});
+ assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 0, 0, 0, 0, 0}, byteArray);
+ // fill - short
+ short[] shortArray = new short[10];
+ ArrayTools.fill(shortArray, 0, 5, new short[] {1, 2, 3, 4, 5, 6});
+ assertArrayEquals(new short[]{1, 2, 3, 4, 5, 0, 0, 0, 0, 0}, shortArray);
+ // fill - int
+ int[] intArray = new int[10];
+ ArrayTools.fill(intArray, 0, 5, new int[] {1, 2, 3, 4, 5, 6});
+ assertArrayEquals(new int[]{1, 2, 3, 4, 5, 0, 0, 0, 0, 0}, intArray);
+ // fill - long
+ long[] longArray = new long[10];
+ ArrayTools.fill(longArray, 0, 5, new long[] {1L, 2L, 3L, 4L, 5L, 6L});
+ assertArrayEquals(new long[]{1L, 2L, 3L, 4L, 5L, 0L, 0L, 0L, 0L, 0L}, longArray);
+ // fill - float
+ float[] floatArray = new float[10];
+ ArrayTools.fill(floatArray, 0, 5, new float[] {1.1F, 2.2F, 3.3F, 4.4F, 5.5F, 6.6F});
+ assertArrayEquals(new float[]{1.1F, 2.2F, 3.3F, 4.4F, 5.5F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}, floatArray);
+ // fill - double
+ double[] doubleArray = new double[10];
+ ArrayTools.fill(doubleArray, 0, 5, new double[] {1.1, 2.2, 3.3, 4.4, 5.5, 6.6});
+ assertArrayEquals(new double[]{1.1, 2.2, 3.3, 4.4, 5.5, 0.0, 0.0, 0.0, 0.0, 0.0}, doubleArray);
+ // fill - T
+ String[] stringArray = new String[10];
+ ArrayTools.fill(stringArray, 0, 5, new String[] {"aaa", "bbb", "ccc", "ddd", "eee", "fff"});
+ assertArrayEquals(new String[]{"aaa", "bbb", "ccc", "ddd", "eee", null, null, null, null, null}, stringArray);
+ }
+
+ // ================================
+ // #endregion
+ // ================================
+
+ // ================================
+ // #region - indexOf
+ // ================================
+
+ @Test
+ void indexOfWithPredicate_NullPredicate_ThrowsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ArrayTools.indexOfWithPredicate(new String[] {}, null));
+ }
+
+ @Test
+ void indexOfWithPredicate_NullArray_ReturnsNotFoundIndex() {
+ Predicate predicate = s -> s.equals("test");
+ int result = ArrayTools.indexOfWithPredicate(null, predicate);
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
+ }
+
+ @Test
+ void indexOfWithPredicate_EmptyArray_ReturnsNotFoundIndex() {
+ Predicate predicate = s -> s.equals("test");
+ int result = ArrayTools.indexOfWithPredicate(new String[] {}, predicate);
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
+ }
+
+ @Test
+ void indexOfWithPredicate_ArrayContainsMatchingElement_ReturnsIndex() {
+ String[] array = { "apple", "banana", "cherry" };
+ Predicate predicate = s -> s.equals("banana");
+ int result = ArrayTools.indexOfWithPredicate(array, predicate);
+ assertEquals(1, result);
+ }
+
+ @Test
+ void indexOfWithPredicate_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() {
+ String[] array = { "apple", "banana", "cherry" };
+ Predicate predicate = s -> s.equals("orange");
+ int result = ArrayTools.indexOfWithPredicate(array, predicate);
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
+ }
+
+ @Test
+ void indexOf_NullArray_ReturnsNotFound() {
+ // T
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((String[]) null, "test"));
+ // char
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((char[]) null, 'a'));
+ // byte
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((byte[]) null, (byte) 1));
+ // short
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((short[]) null, (short) 1));
+ // int
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((int[]) null, 1));
+ // long
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((long[]) null, 1L));
+ // float
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((float[]) null, 1.23F));
+ // double
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf((double[]) null, 1.23));
+ }
+
+ @Test
+ void indexOf_EmptyArray_ReturnsNotFound() {
+ // T
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] {}, "test"));
+ // char
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new char[] {}, 'a'));
+ // byte
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new byte[] {}, (byte) 1));
+ // short
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new short[] {}, (short) 1));
+ // int
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new int[] {}, 1));
+ // long
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new long[] {}, 1L));
+ // float
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new float[] {}, 1.23F));
+ // double
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new double[] {}, 1.23));
+ }
+
+ @Test
+ void indexOf_ObjectFound_ReturnsIndex() {
+ // T
+ assertEquals(1, ArrayTools.indexOf(new String[] { "a", "b", "c" }, "b"));
+ // char
+ assertEquals(1, ArrayTools.indexOf(new char[] { 'a', 'b', 'c' }, 'b'));
+ // byte
+ assertEquals(1, ArrayTools.indexOf(new byte[] { 1, 2, 3 }, (byte) 2));
+ // short
+ assertEquals(1, ArrayTools.indexOf(new short[] { 1, 2, 3 }, (short) 2));
+ // int
+ assertEquals(1, ArrayTools.indexOf(new int[] { 1, 2, 3 }, 2));
+ // long
+ assertEquals(1, ArrayTools.indexOf(new long[] { 1000000L, 2000000L, 3000000L }, 2000000L));
+ // float
+ assertEquals(1, ArrayTools.indexOf(new float[] { 1.11F, 2.22F, 3.33F }, 2.22F));
+ // double
+ assertEquals(1, ArrayTools.indexOf(new double[] { 1.11, 2.22, 3.33 }, 2.22));
+ }
+
+ @Test
+ void indexOf_ObjectNotFound_ReturnsNotFound() {
+ // T
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] { "a", "b", "c" }, "d"));
+ // char
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new char[] { 'a', 'b', 'c' }, 'd'));
+ // byte
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new byte[] { 1, 2, 3 }, (byte) 4));
+ // short
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new short[] { 1, 2, 3 }, (short) 4));
+ // int
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new int[] { 1, 2, 3 }, 4));
+ // long
+ assertEquals(ArrayTools.NOT_FOUND_INDEX,
+ ArrayTools.indexOf(new long[] { 1000000L, 2000000L, 3000000L }, 4000000L));
+ // float
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new float[] { 1.11F, 2.22F, 3.33F }, 4.44F));
+ // double
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new double[] { 1.11, 2.22, 3.33 }, 4.44));
+ }
+
+ @Test
+ void indexOf_NullObject_ReturnsNotFound() {
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] { "a", "b", "c" }, null));
+ }
+
+ @Test
+ void indexOf_ArrayContainsNull_ReturnsIndex() {
+ assertEquals(1, ArrayTools.indexOf(new String[] { "a", null, "c" }, null));
+ }
+
+ // ================================
+ // #endregion
+ // ================================
+
+ // ================================
+ // #region - lastIndexOf
+ // ================================
+
+ @Test
+ void lastIndexOfWithPredicate_NullPredicate_ThrowsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ArrayTools.lastIndexOfWithPredicate(new String[] {}, null));
+ }
+
+ @Test
+ void lastIndexOfWithPredicate_NullArray_ReturnsNotFoundIndex() {
+ Predicate predicate = s -> s.equals("test");
+ int result = ArrayTools.lastIndexOfWithPredicate(null, predicate);
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
+ }
+
+ @Test
+ void lastIndexOfWithPredicate_EmptyArray_ReturnsNotFoundIndex() {
+ Predicate predicate = s -> s.equals("test");
+ int result = ArrayTools.lastIndexOfWithPredicate(new String[] {}, predicate);
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
+ }
+
+ @Test
+ void lastIndexOfWithPredicate_ArrayContainsMatchingElement_ReturnsIndex() {
+ String[] array = { "apple", "banana", "banana", "cherry" };
+ Predicate predicate = s -> s.equals("banana");
+ int result = ArrayTools.lastIndexOfWithPredicate(array, predicate);
+ assertEquals(2, result);
+ }
+
+ @Test
+ void lastIndexOfWithPredicate_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() {
+ String[] array = { "apple", "banana", "cherry" };
+ Predicate predicate = s -> s.equals("orange");
+ int result = ArrayTools.lastIndexOfWithPredicate(array, predicate);
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
+ }
+
+ @Test
+ void lastIndexOf_NullArray_ReturnsNotFound() {
+ // T
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((String[]) null, "test"));
+ // char
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((char[]) null, 'a'));
+ // byte
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((byte[]) null, (byte) 1));
+ // short
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((short[]) null, (short) 1));
+ // int
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((int[]) null, 1));
+ // long
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((long[]) null, 1L));
+ // float
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((float[]) null, 1.23F));
+ // double
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf((double[]) null, 1.23));
+ }
+
+ @Test
+ void lastIndexOf_EmptyArray_ReturnsNotFound() {
+ // T
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] {}, "test"));
+ // char
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new char[] {}, 'a'));
+ // byte
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new byte[] {}, (byte) 1));
+ // short
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new short[] {}, (short) 1));
+ // int
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new int[] {}, 1));
+ // long
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new long[] {}, 1L));
+ // float
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new float[] {}, 1.23F));
+ // double
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new double[] {}, 1.23));
+ }
+
+ @Test
+ void lastIndexOf_ObjectFound_ReturnsIndex() {
+ // T
+ assertEquals(3, ArrayTools.lastIndexOf(new String[] { "a", "b", "c", "b", "c" }, "b"));
+ // char
+ assertEquals(3, ArrayTools.lastIndexOf(new char[] { 'a', 'b', 'c', 'b', 'c' }, 'b'));
+ // byte
+ assertEquals(3, ArrayTools.lastIndexOf(new byte[] { 1, 2, 3, 2, 3 }, (byte) 2));
+ // short
+ assertEquals(3, ArrayTools.lastIndexOf(new short[] { 1, 2, 3, 2, 3 }, (short) 2));
+ // int
+ assertEquals(3, ArrayTools.lastIndexOf(new int[] { 1, 2, 3, 2, 3 }, 2));
+ // long
+ assertEquals(3,
+ ArrayTools.lastIndexOf(new long[] { 1000000L, 2000000L, 3000000L, 2000000L, 3000000L }, 2000000L));
+ // float
+ assertEquals(3, ArrayTools.lastIndexOf(new float[] { 1.11F, 2.22F, 3.33F, 2.22F, 3.33F }, 2.22F));
+ // double
+ assertEquals(3, ArrayTools.lastIndexOf(new double[] { 1.11, 2.22, 3.33, 2.22, 3.33 }, 2.22));
+ }
+
+ @Test
+ void lastIndexOf_ObjectNotFound_ReturnsNotFound() {
+ // T
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] { "a", "b", "c" }, "d"));
+ // char
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new char[] { 'a', 'b', 'c' }, 'd'));
+ // byte
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new byte[] { 1, 2, 3 }, (byte) 4));
+ // short
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new short[] { 1, 2, 3 }, (short) 4));
+ // int
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new int[] { 1, 2, 3 }, 4));
+ // long
+ assertEquals(ArrayTools.NOT_FOUND_INDEX,
+ ArrayTools.lastIndexOf(new long[] { 1000000L, 2000000L, 3000000L }, 4000000L));
+ // float
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new float[] { 1.11F, 2.22F, 3.33F }, 4.44F));
+ // double
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new double[] { 1.11, 2.22, 3.33 }, 4.44));
+ }
+
+ @Test
+ void lastIndexOf_NullObject_ReturnsNotFound() {
+ assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] { "a", "b", "c" }, null));
+ }
+
+ @Test
+ void lastIndexOf_ArrayContainsNull_ReturnsIndex() {
+ assertEquals(3, ArrayTools.lastIndexOf(new String[] { "a", null, "c", null, "e" }, null));
+ }
+
+ // ================================
+ // #endregion
+ // ================================
+
+ // ================================
+ // #region - contains
+ // ================================
+
+ @Test
+ void contains_NullArray_ReturnsFalse() {
+ assertFalse(ArrayTools.contains((String[]) null, "test"));
+ // char
+ assertFalse(ArrayTools.contains((char[]) null, 'a'));
+ // byte
+ assertFalse(ArrayTools.contains((byte[]) null, (byte) 1));
+ // short
+ assertFalse(ArrayTools.contains((short[]) null, (short) 1));
+ // int
+ assertFalse(ArrayTools.contains((int[]) null, 1));
+ // long
+ assertFalse(ArrayTools.contains((long[]) null, 1L));
+ // float
+ assertFalse(ArrayTools.contains((float[]) null, 1.2F));
+ // double
+ assertFalse(ArrayTools.contains((double[]) null, 1.2));
+ }
+
+ @Test
+ void contains_EmptyArray_ReturnsFalse() {
+ assertFalse(ArrayTools.contains(new String[] {}, "test"));
+ // char
+ assertFalse(ArrayTools.contains(new char[] {}, 'a'));
+ // byte
+ assertFalse(ArrayTools.contains(new byte[] {}, (byte) 1));
+ // short
+ assertFalse(ArrayTools.contains(new short[] {}, (short) 1));
+ // int
+ assertFalse(ArrayTools.contains(new int[] {}, 1));
+ // long
+ assertFalse(ArrayTools.contains(new long[] {}, 1L));
+ // float
+ assertFalse(ArrayTools.contains(new float[] {}, 1.2F));
+ // double
+ assertFalse(ArrayTools.contains(new double[] {}, 1.2));
+ }
+
+ @Test
+ void contains_ArrayContainsObject_ReturnsTrue() {
+ assertTrue(ArrayTools.contains(new String[] { "test", "example" }, "test"));
+ // char
+ assertTrue(ArrayTools.contains(new char[] { 'a', 'b', 'c' }, 'a'));
+ // byte
+ assertTrue(ArrayTools.contains(new byte[] { 1, 2, 3 }, (byte) 1));
+ // short
+ assertTrue(ArrayTools.contains(new short[] { 1, 2, 3 }, (short) 1));
+ // int
+ assertTrue(ArrayTools.contains(new int[] { 1, 2, 3 }, 1));
+ // long
+ assertTrue(ArrayTools.contains(new long[] { 1000000L, 2000000L, 3000000L }, 1000000L));
+ // float
+ assertTrue(ArrayTools.contains(new float[] { 1.11F, 2.22F, 3.33F }, 2.22F));
+ // double
+ assertTrue(ArrayTools.contains(new double[] { 1.11, 2.22, 3.33 }, 2.22));
+ }
+
+ @Test
+ void contains_ArrayDoesNotContainObject_ReturnsFalse() {
+ // T
+ assertFalse(ArrayTools.contains(new String[] { "a", "b", "c" }, "d"));
+ // char
+ assertFalse(ArrayTools.contains(new char[] { 'a', 'b', 'c' }, 'd'));
+ // byte
+ assertFalse(ArrayTools.contains(new byte[] { 1, 2, 3 }, (byte) 4));
+ // short
+ assertFalse(ArrayTools.contains(new short[] { 1, 2, 3 }, (short) 4));
+ // int
+ assertFalse(ArrayTools.contains(new int[] { 1, 2, 3 }, 4));
+ // long
+ assertFalse(ArrayTools.contains(new long[] { 1000000L, 2000000L, 3000000L }, 4000000L));
+ // float
+ assertFalse(ArrayTools.contains(new float[] { 1.11F, 2.22F, 3.33F }, 4.44F));
+ // double
+ assertFalse(ArrayTools.contains(new double[] { 1.11, 2.22, 3.33 }, 4.44));
+ }
+
+ @Test
+ void contains_ObjectIsNull_ReturnsFalse() {
+ assertFalse(ArrayTools.contains(new String[] { "test", "example" }, null));
+ }
+
+ @Test
+ void contains_ArrayContainsNull_ReturnsTrue() {
+ assertTrue(ArrayTools.contains(new String[] { "test", null }, null));
+ }
+
+ // ================================
+ // #endregion
+ // ================================
+
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java
new file mode 100644
index 0000000..b6ea20e
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java
@@ -0,0 +1,942 @@
+/*
+ * Copyright 2024 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.util;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.time.LocalDate;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import org.junit.jupiter.api.Test;
+
+import xyz.zhouxy.plusone.commons.exception.DataNotExistsException;
+import xyz.zhouxy.plusone.commons.exception.system.DataOperationResultException;
+
+@SuppressWarnings("null")
+public class AssertToolsTests {
+
+ // #region - Argument
+
+ @Test
+ void testCheckArgument_true() {
+ AssertTools.checkArgument(true);
+ }
+
+ @Test
+ void testCheckArgument_true_withMessage() {
+ final String IGNORE_ME = "IGNORE_ME"; // NOSONAR
+ AssertTools.checkArgument(true, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckArgument_true_withNullMessage() {
+ final String IGNORE_ME = null; // NOSONAR
+ AssertTools.checkArgument(true, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckArgument_true_withMessageSupplier() {
+ AssertTools.checkArgument(true, () -> "Error message: " + LocalDate.now());
+ }
+
+ @Test
+ void testCheckArgument_true_withNullMessageSupplier() {
+ final Supplier IGNORE_ME = null; // NOSONAR
+ AssertTools.checkArgument(true, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckArgument_true_withMessageFormat() {
+ LocalDate today = LocalDate.now();
+ AssertTools.checkArgument(true, "String format: %s", today);
+ }
+
+ @Test
+ void testCheckArgument_true_withNullMessageFormat() {
+ AssertTools.checkArgument(true, null, LocalDate.now());
+ }
+
+ @Test
+ void testCheckArgument_false() {
+ final IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> AssertTools.checkArgument(false));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckArgument_false_withMessage() {
+ final String message = "testCheckArgument_false_withMessage";
+
+ final IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> AssertTools.checkArgument(false, message));
+
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckArgument_false_withNullMessage() {
+ final String message = null;
+
+ final IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> AssertTools.checkArgument(false, message));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckArgument_false_withMessageSupplier() {
+ final LocalDate today = LocalDate.now();
+ final IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> AssertTools.checkArgument(false, () -> "Error message: " + today));
+
+ assertEquals("Error message: " + today, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckArgument_false_withNullMessageSupplier() {
+ Supplier messageSupplier = null;
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkArgument(false, messageSupplier));
+ }
+
+ @Test
+ void testCheckArgument_false_withMessageFormat() {
+ LocalDate today = LocalDate.now();
+ final IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> AssertTools.checkArgument(false, "String format: %s", today));
+ assertEquals(String.format("String format: %s", today), e.getMessage());
+ }
+
+ @Test
+ void testCheckArgument_false_withNullMessageFormat() {
+ LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkArgument(false, null, today));
+ }
+
+ // #endregion - Argument
+
+ // #region - State
+
+ @Test
+ void testCheckState_true() {
+ AssertTools.checkState(true);
+ }
+
+ @Test
+ void testCheckState_true_withMessage() {
+ final String IGNORE_ME = "IGNORE_ME"; // NOSONAR
+ AssertTools.checkState(true, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckState_true_withNullMessage() {
+ final String IGNORE_ME = null; // NOSONAR
+ AssertTools.checkState(true, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckState_true_withMessageSupplier() {
+ AssertTools.checkState(true, () -> "Error message: " + LocalDate.now());
+ }
+
+ @Test
+ void testCheckState_true_withNullMessageSupplier() {
+ final Supplier IGNORE_ME = null; // NOSONAR // NOSONAR
+ AssertTools.checkState(true, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckState_true_withMessageFormat() {
+ LocalDate today = LocalDate.now();
+ AssertTools.checkState(true, "String format: %s", today);
+ }
+
+ @Test
+ void testCheckState_true_withNullMessageFormat() {
+ AssertTools.checkState(true, null, LocalDate.now());
+ }
+
+ @Test
+ void testCheckState_false() {
+ final IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> AssertTools.checkState(false));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckState_false_withMessage() {
+ final String message = "testCheckState_false_withMessage";
+
+ final IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> AssertTools.checkState(false, message));
+
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckState_false_withNullMessage() {
+ final String message = null;
+
+ final IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> AssertTools.checkState(false, message));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckState_false_withMessageSupplier() {
+ final LocalDate today = LocalDate.now();
+ final IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> AssertTools.checkState(false, () -> "Error message: " + today));
+
+ assertEquals("Error message: " + today, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckState_false_withNullMessageSupplier() {
+ Supplier messageSupplier = null;
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkState(false, messageSupplier));
+ }
+
+ @Test
+ void testCheckState_false_withMessageFormat() {
+ LocalDate today = LocalDate.now();
+ final IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> AssertTools.checkState(false, "String format: %s", today));
+ assertEquals(String.format("String format: %s", today), e.getMessage());
+ }
+
+ @Test
+ void testCheckState_false_withNullMessageFormat() {
+ LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkState(false, null, today));
+ }
+
+ // #endregion - State
+
+ // #region - NotNull
+
+ @Test
+ void testCheckNotNull_notNull() {
+ final Object object = new Object();
+ AssertTools.checkNotNull(object);
+ }
+
+ @Test
+ void testCheckNotNull_notNull_withMessage() {
+ final Object object = new Object();
+ final String IGNORE_ME = "IGNORE_ME"; // NOSONAR
+ AssertTools.checkNotNull(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckNotNull_notNull_withNullMessage() {
+ final Object object = new Object();
+ final String IGNORE_ME = null; // NOSONAR
+ AssertTools.checkNotNull(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckNotNull_notNull_withMessageSupplier() {
+ final Object object = new Object();
+ AssertTools.checkNotNull(object, () -> "Error message: " + LocalDate.now());
+ }
+
+ @Test
+ void testCheckNotNull_notNull_withNullMessageSupplier() {
+ final Object object = new Object();
+ final Supplier IGNORE_ME = null; // NOSONAR // NOSONAR
+ AssertTools.checkNotNull(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckNotNull_notNull_withMessageFormat() {
+ final Object object = new Object();
+ LocalDate today = LocalDate.now();
+ AssertTools.checkNotNull(object, "String format: %s", today);
+ }
+
+ @Test
+ void testCheckNotNull_notNull_withNullMessageFormat() {
+ final Object object = new Object();
+ AssertTools.checkNotNull(object, null, LocalDate.now());
+ }
+
+ @Test
+ void testCheckNotNull_null() {
+ final Object object = null;
+ final NullPointerException e = assertThrows(NullPointerException.class,
+ () -> AssertTools.checkNotNull(object));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckNotNull_null_withMessage() {
+ final Object object = null;
+ final String message = "testCheckNotNull_null_withMessage";
+
+ final NullPointerException e = assertThrows(NullPointerException.class,
+ () -> AssertTools.checkNotNull(object, message));
+
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckNotNull_null_withNullMessage() {
+ final Object object = null;
+ final String message = null;
+
+ final NullPointerException e = assertThrows(NullPointerException.class,
+ () -> AssertTools.checkNotNull(object, message));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckNotNull_null_withMessageSupplier() {
+ final Object object = null;
+ final LocalDate today = LocalDate.now();
+ final NullPointerException e = assertThrows(NullPointerException.class,
+ () -> AssertTools.checkNotNull(object, () -> "Error message: " + today));
+
+ assertEquals("Error message: " + today, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckNotNull_null_withNullMessageSupplier() {
+ final Object object = null;
+ Supplier messageSupplier = null;
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkNotNull(object, messageSupplier));
+ }
+
+ @Test
+ void testCheckNotNull_null_withMessageFormat() {
+ final Object object = null;
+ LocalDate today = LocalDate.now();
+ final NullPointerException e = assertThrows(NullPointerException.class,
+ () -> AssertTools.checkNotNull(object, "String format: %s", today));
+ assertEquals(String.format("String format: %s", today), e.getMessage());
+ }
+
+ @Test
+ void testCheckNotNull_null_withNullMessageFormat() {
+ final Object object = null;
+ LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkNotNull(object, null, today));
+ }
+
+ // #endregion - NotNull
+
+ // #region - Exists
+
+ @Test
+ void testCheckExists_notNull() throws DataNotExistsException {
+ final Object object = new Object();
+ AssertTools.checkExists(object);
+ }
+
+ @Test
+ void testCheckExists_notNull_withMessage() throws DataNotExistsException {
+ final Object object = new Object();
+ final String IGNORE_ME = "IGNORE_ME"; // NOSONAR
+ AssertTools.checkExists(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckExists_notNull_withNullMessage() throws DataNotExistsException {
+ final Object object = new Object();
+ final String IGNORE_ME = null; // NOSONAR
+ AssertTools.checkExists(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckExists_notNull_withMessageSupplier() throws DataNotExistsException {
+ final Object object = new Object();
+ AssertTools.checkExists(object, () -> "Error message: " + LocalDate.now());
+ }
+
+ @Test
+ void testCheckExists_notNull_withNullMessageSupplier() throws DataNotExistsException {
+ final Object object = new Object();
+ final Supplier IGNORE_ME = null; // NOSONAR // NOSONAR
+ AssertTools.checkExists(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckExists_notNull_withMessageFormat() throws DataNotExistsException {
+ final Object object = new Object();
+ LocalDate today = LocalDate.now();
+ AssertTools.checkExists(object, "String format: %s", today);
+ }
+
+ @Test
+ void testCheckExists_notNull_withNullMessageFormat() throws DataNotExistsException {
+ final Object object = new Object();
+ AssertTools.checkExists(object, null, LocalDate.now());
+ }
+
+ @Test
+ void testCheckExists_null() {
+ final Object object = null;
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_null_withMessage() {
+ final Object object = null;
+ final String message = "testCheckExists_null_withMessage";
+
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, message));
+
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_null_withNullMessage() {
+ final Object object = null;
+ final String message = null;
+
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, message));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_null_withMessageSupplier() {
+ final Object object = null;
+ final LocalDate today = LocalDate.now();
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, () -> "Error message: " + today));
+
+ assertEquals("Error message: " + today, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_null_withNullMessageSupplier() {
+ final Object object = null;
+ Supplier messageSupplier = null;
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, messageSupplier));
+ }
+
+ @Test
+ void testCheckExists_null_withMessageFormat() {
+ final Object object = null;
+ LocalDate today = LocalDate.now();
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, "String format: %s", today));
+ assertEquals(String.format("String format: %s", today), e.getMessage());
+ }
+
+ @Test
+ void testCheckExists_null_withNullMessageFormat() {
+ final Object object = null;
+ LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, null, today));
+ }
+
+ @Test
+ void testCheckExists_optional() throws DataNotExistsException {
+ final Optional object = Optional.of(new Object());
+ AssertTools.checkExists(object);
+ }
+
+ @Test
+ void testCheckExists_optional_withMessage() throws DataNotExistsException {
+ final Optional object = Optional.of(new Object());
+ final String IGNORE_ME = "IGNORE_ME"; // NOSONAR
+ AssertTools.checkExists(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckExists_optional_withNullMessage() throws DataNotExistsException {
+ final Optional object = Optional.of(new Object());
+ final String IGNORE_ME = null; // NOSONAR
+ AssertTools.checkExists(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckExists_optional_withMessageSupplier() throws DataNotExistsException {
+ final Optional object = Optional.of(new Object());
+ AssertTools.checkExists(object, () -> "Error message: " + LocalDate.now());
+ }
+
+ @Test
+ void testCheckExists_optional_withNullMessageSupplier() throws DataNotExistsException {
+ final Optional object = Optional.of(new Object());
+ final Supplier IGNORE_ME = null; // NOSONAR
+ AssertTools.checkExists(object, IGNORE_ME);
+ }
+
+ @Test
+ void testCheckExists_optional_withMessageFormat() throws DataNotExistsException {
+ final Optional object = Optional.of(new Object());
+ LocalDate today = LocalDate.now();
+ AssertTools.checkExists(object, "String format: %s", today);
+ }
+
+ @Test
+ void testCheckExists_optional_withNullMessageFormat() throws DataNotExistsException {
+ final Optional object = Optional.of(new Object());
+ AssertTools.checkExists(object, null, LocalDate.now());
+ }
+
+ @Test
+ void testCheckExists_emptyOptional() {
+ final Optional object = Optional.empty();
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_emptyOptional_withMessage() {
+ final Optional object = Optional.empty();
+ final String message = "testCheckExists_emptyOptional_withMessage";
+
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, message));
+
+ assertEquals(message, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_emptyOptional_withNullMessage() {
+ final Optional object = Optional.empty();
+ final String message = null;
+
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, message));
+
+ assertNull(e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_emptyOptional_withMessageSupplier() {
+ final Optional object = Optional.empty();
+ final LocalDate today = LocalDate.now();
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, () -> "Error message: " + today));
+
+ assertEquals("Error message: " + today, e.getMessage());
+ assertNull(e.getCause());
+ }
+
+ @Test
+ void testCheckExists_emptyOptional_withNullMessageSupplier() {
+ final Optional object = Optional.empty();
+ Supplier messageSupplier = null;
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, messageSupplier));
+ }
+
+ @Test
+ void testCheckExists_emptyOptional_withMessageFormat() {
+ final Optional object = Optional.empty();
+ LocalDate today = LocalDate.now();
+ final DataNotExistsException e = assertThrows(DataNotExistsException.class,
+ () -> AssertTools.checkExists(object, "String format: %s", today));
+ assertEquals(String.format("String format: %s", today), e.getMessage());
+ }
+
+ @Test
+ void testCheckExists_emptyOptional_withNullMessageFormat() {
+ final Optional object = Optional.empty();
+ LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, null, today));
+ }
+
+ @Test
+ void testCheckExists_nullOptional() {
+ final Optional object = null;
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object));
+ }
+
+ @Test
+ void testCheckExists_nullOptional_withMessage() {
+ final Optional object = null;
+ final String message = "testCheckExists_nullOptional_withMessage";
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, message));
+ }
+
+ @Test
+ void testCheckExists_nullOptional_withNullMessage() {
+ final Optional object = null;
+ final String message = null;
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, message));
+ }
+
+ @Test
+ void testCheckExists_nullOptional_withMessageSupplier() {
+ final Optional object = null;
+ final LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, () -> "Error message: " + today));
+ }
+
+ @Test
+ void testCheckExists_nullOptional_withNullMessageSupplier() {
+ final Optional object = null;
+ Supplier messageSupplier = null;
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, messageSupplier));
+ }
+
+ @Test
+ void testCheckExists_nullOptional_withMessageFormat() {
+ final Optional object = null;
+ LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, "String format: %s", today));
+ }
+
+ @Test
+ void testCheckExists_nullOptional_withNullMessageFormat() {
+ final Optional object = null;
+ LocalDate today = LocalDate.now();
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkExists(object, null, today));
+ }
+
+ // #endregion - Exists
+
+ // #region - AffectedRows
+
+ @Test
+ void testCheckAffectedRows_int() {
+ final int expectedValue = 25;
+
+ AssertTools.checkAffectedRows(expectedValue, 25);
+
+ DataOperationResultException e0 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108));
+ assertEquals(String.format("The number of rows affected is expected to be %d, but is: %d", expectedValue, 108),
+ e0.getMessage());
+
+ }
+
+ @Test
+ void testCheckAffectedRows_int_message() {
+ final int expectedValue = 25;
+
+ final String message = "Static message";
+
+ AssertTools.checkAffectedRows(expectedValue, 25, message);
+
+ DataOperationResultException e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108, message));
+ assertEquals(message, e1.getMessage());
+
+ final String nullMessage = null;
+
+ AssertTools.checkAffectedRows(expectedValue, 25, nullMessage);
+
+ e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108, nullMessage));
+ assertNull(e1.getMessage());
+ }
+
+ @Test
+ void testCheckAffectedRows_int_messageSupplier() {
+ final int expectedValue = 25;
+
+ final Supplier messageSupplier = () -> "Supplier message";
+
+ AssertTools.checkAffectedRows(expectedValue, 25, messageSupplier);
+
+ DataOperationResultException e2 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108, messageSupplier));
+ assertEquals(messageSupplier.get(), e2.getMessage());
+
+ final Supplier nullSupplier = null;
+
+ AssertTools.checkAffectedRows(expectedValue, 25, nullSupplier);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108, nullSupplier));
+ }
+
+ @Test
+ void testCheckAffectedRows_int_messageFormat() {
+ final int expectedValue = 25;
+
+ AssertTools.checkAffectedRows(expectedValue, 25, "预计是 %d,结果是 %d。", expectedValue, 25);
+
+ DataOperationResultException e3 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108, "预计是 %d,结果是 %d。", expectedValue, 108));
+ assertEquals("预计是 25,结果是 108。", e3.getMessage());
+
+ AssertTools.checkAffectedRows(expectedValue, 25, null, expectedValue, 25);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108, null, expectedValue, 108));
+ }
+
+ @Test
+ void testCheckAffectedRows_long() {
+ final long expectedValue = 25L;
+
+ AssertTools.checkAffectedRows(expectedValue, 25L);
+
+ DataOperationResultException e0 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108L));
+ assertEquals(String.format("The number of rows affected is expected to be %d, but is: %d", expectedValue, 108L),
+ e0.getMessage());
+
+ }
+
+ @Test
+ void testCheckAffectedRows_long_message() {
+ final long expectedValue = 25L;
+
+ final String message = "Static message";
+
+ AssertTools.checkAffectedRows(expectedValue, 25L, message);
+
+ DataOperationResultException e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108L, message));
+ assertEquals(message, e1.getMessage());
+
+ final String nullMessage = null;
+
+ AssertTools.checkAffectedRows(expectedValue, 25L, nullMessage);
+
+ e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108L, nullMessage));
+ assertNull(e1.getMessage());
+ }
+
+ @Test
+ void testCheckAffectedRows_long_messageSupplier() {
+ final long expectedValue = 25L;
+
+ final Supplier messageSupplier = () -> "Supplier message";
+
+ AssertTools.checkAffectedRows(expectedValue, 25L, messageSupplier);
+
+ DataOperationResultException e2 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108L, messageSupplier));
+ assertEquals(messageSupplier.get(), e2.getMessage());
+
+ final Supplier nullSupplier = null;
+
+ AssertTools.checkAffectedRows(expectedValue, 25L, nullSupplier);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108L, nullSupplier));
+ }
+
+ @Test
+ void testCheckAffectedRows_long_messageFormat() {
+ final long expectedValue = 25L;
+
+ AssertTools.checkAffectedRows(expectedValue, 25L, "预计是 %d,结果是 %d。", expectedValue, 25L);
+
+ DataOperationResultException e3 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108L, "预计是 %d,结果是 %d。", expectedValue, 108L));
+ assertEquals("预计是 25,结果是 108。", e3.getMessage());
+
+ AssertTools.checkAffectedRows(expectedValue, 25L, null, expectedValue, 25L);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedRows(expectedValue, 108L, null, expectedValue, 108L));
+ }
+
+ @Test
+ void testCheckAffectedOneRow_int() {
+ AssertTools.checkAffectedOneRow(1);
+
+ DataOperationResultException e0 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108));
+ assertEquals(String.format("The number of rows affected is expected to be 1, but is: %d", 108),
+ e0.getMessage());
+
+ }
+
+ @Test
+ void testCheckAffectedOneRow_int_message() {
+ final String message = "Static message";
+
+ AssertTools.checkAffectedOneRow(1, message);
+
+ DataOperationResultException e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108, message));
+ assertEquals(message, e1.getMessage());
+
+ final String nullMessage = null;
+
+ AssertTools.checkAffectedOneRow(1, nullMessage);
+
+ e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108, nullMessage));
+ assertNull(e1.getMessage());
+ }
+
+ @Test
+ void testCheckAffectedOneRow_int_messageSupplier() {
+ final Supplier messageSupplier = () -> "Supplier message";
+
+ AssertTools.checkAffectedOneRow(1, messageSupplier);
+
+ DataOperationResultException e2 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108, messageSupplier));
+ assertEquals(messageSupplier.get(), e2.getMessage());
+
+ final Supplier nullSupplier = null;
+
+ AssertTools.checkAffectedOneRow(1, nullSupplier);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedOneRow(108, nullSupplier));
+ }
+
+ @Test
+ void testCheckAffectedOneRow_int_messageFormat() {
+ AssertTools.checkAffectedOneRow(1, "预计是 %d,结果是 %d。", 1, 108);
+
+ DataOperationResultException e3 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108, "预计是 %d,结果是 %d。", 1, 108));
+ assertEquals("预计是 1,结果是 108。", e3.getMessage());
+
+ AssertTools.checkAffectedOneRow(1, null, 1);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedOneRow(108, null, 108));
+ }
+
+ @Test
+ void testCheckAffectedOneRow_long() {
+ AssertTools.checkAffectedOneRow(1L);
+
+ DataOperationResultException e0 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108L));
+ assertEquals(String.format("The number of rows affected is expected to be 1, but is: %d", 108L),
+ e0.getMessage());
+
+ }
+
+ @Test
+ void testCheckAffectedOneRow_long_message() {
+ final String message = "Static message";
+
+ AssertTools.checkAffectedOneRow(1L, message);
+
+ DataOperationResultException e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108L, message));
+ assertEquals(message, e1.getMessage());
+
+ final String nullMessage = null;
+
+ AssertTools.checkAffectedOneRow(1L, nullMessage);
+
+ e1 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108L, nullMessage));
+ assertNull(e1.getMessage());
+ }
+
+ @Test
+ void testCheckAffectedOneRow_long_messageSupplier() {
+ final Supplier messageSupplier = () -> "Supplier message";
+
+ AssertTools.checkAffectedOneRow(1L, messageSupplier);
+
+ DataOperationResultException e2 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108L, messageSupplier));
+ assertEquals(messageSupplier.get(), e2.getMessage());
+
+ final Supplier nullSupplier = null;
+
+ AssertTools.checkAffectedOneRow(1L, nullSupplier);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedOneRow(108L, nullSupplier));
+ }
+
+ @Test
+ void testCheckAffectedOneRow_long_messageFormat() {
+ AssertTools.checkAffectedOneRow(1L, "预计是 %d,结果是 %d。", 1L, 108L);
+
+ DataOperationResultException e3 = assertThrows(DataOperationResultException.class,
+ () -> AssertTools.checkAffectedOneRow(108L, "预计是 %d,结果是 %d。", 1L, 108L));
+ assertEquals("预计是 1,结果是 108。", e3.getMessage());
+
+ AssertTools.checkAffectedOneRow(1L, null, 1L);
+
+ assertThrows(NullPointerException.class,
+ () -> AssertTools.checkAffectedOneRow(108L, null, 108L));
+ }
+
+ // #endregion - AffectedRows
+
+ // #region - Condition
+
+ static final class MyException extends RuntimeException {}
+
+ @Test
+ void testCheckCondition() {
+
+ AssertTools.checkCondition(true, MyException::new);
+
+ final MyException me = new MyException();
+ MyException e = assertThrows(MyException.class, () -> AssertTools.checkCondition(false, () -> me));
+ assertEquals(me, e);
+ }
+
+ // #endregion - Condition
+
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java
index 21f25c6..2fc6a0c 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/BigDecimalsTests.java
@@ -16,11 +16,9 @@
package xyz.zhouxy.plusone.commons.util;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.*;
import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.function.Function;
import org.junit.jupiter.api.Test;
@@ -30,75 +28,178 @@ import lombok.extern.slf4j.Slf4j;
public class BigDecimalsTests {
@Test
- void testToPlainString() {
-
- BigDecimalFormatter formatter = BigDecimalFormatter.builder()
- .setScale(2, RoundingMode.HALF_UP)
- .stripTrailingZeros()
- .build();
-
- assertEquals("8.09", formatter.toPlainString(BigDecimals.of("8.090")));
- assertEquals("8.09", formatter.toPlainString(BigDecimals.of("8.094")));
- assertEquals("8.1", formatter.toPlainString(BigDecimals.of("8.095")));
- assertEquals("8.1", formatter.toPlainString(BigDecimals.of("8.096")));
- assertEquals("8.1", formatter.toPlainString(BigDecimals.of("8.100")));
+ void equalsValue_NullValues_ReturnsTrue() {
+ assertTrue(BigDecimals.equalsValue(null, null));
}
@Test
- void test() {
- Object a = 100 % 3.0;
- log.info("a: {}", a);
- }
-}
-
-class BigDecimalFormatter {
- private final Function func;
-
- private BigDecimalFormatter(Function wholeFunc) {
- this.func = wholeFunc;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public String toPlainString(BigDecimal value) {
- final BigDecimal finalDecimal = func == null ? value : func.apply(value);
- return finalDecimal.toPlainString();
- }
-
- public String toEngineeringString(BigDecimal value) {
- final BigDecimal finalDecimal = func == null ? value : func.apply(value);
- return finalDecimal.toEngineeringString();
- }
-
- public static class Builder {
- private Function wholeFunc;
-
- private Builder() {
- }
-
- public Builder setScale(int newScale, RoundingMode roundingMode) {
- final Function func = value -> value.setScale(newScale, roundingMode);
- if (wholeFunc == null) {
- wholeFunc = func;
- } else {
- wholeFunc = func.andThen(func);
- }
- return this;
- }
-
- public Builder stripTrailingZeros() {
- if (wholeFunc == null) {
- wholeFunc = BigDecimal::stripTrailingZeros;
- } else {
- wholeFunc = wholeFunc.andThen(BigDecimal::stripTrailingZeros);
- }
- return this;
- }
-
- public BigDecimalFormatter build() {
- return new BigDecimalFormatter(wholeFunc);
- }
+ void equalsValue_SameNonNullableValues_ReturnsTrue() {
+ BigDecimal bd1 = new BigDecimal("10");
+ BigDecimal bd2 = new BigDecimal("10.0");
+ assertTrue(BigDecimals.equalsValue(bd1, bd2));
}
+
+ @Test
+ void equalsValue_DifferentNonNullableValues_ReturnsFalse() {
+ BigDecimal bd1 = new BigDecimal("10");
+ BigDecimal bd2 = new BigDecimal("20");
+ assertFalse(BigDecimals.equalsValue(bd1, bd2));
+ }
+
+ @Test
+ void equalsValue_OneNullOneNonNullValue_ReturnsFalse() {
+ BigDecimal bd = new BigDecimal("10");
+ assertFalse(BigDecimals.equalsValue(bd, null));
+ assertFalse(BigDecimals.equalsValue(null, bd));
+ }
+
+ @Test
+ void gt_NullFirstValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.gt(null, bd));
+ }
+
+ @Test
+ void gt_NullSecondValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.gt(bd, null));
+ }
+
+ @Test
+ void gt_SameValues_ReturnsFalse() {
+ BigDecimal bd = new BigDecimal("10");
+ assertFalse(BigDecimals.gt(bd, bd));
+ }
+
+ @Test
+ void gt_FirstGreaterThanSecond_ReturnsTrue() {
+ BigDecimal bd1 = new BigDecimal("20");
+ BigDecimal bd2 = new BigDecimal("10");
+ assertTrue(BigDecimals.gt(bd1, bd2));
+ }
+
+ @Test
+ void ge_NullFirstValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.ge(null, bd));
+ }
+
+ @Test
+ void ge_NullSecondValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.ge(bd, null));
+ }
+
+ @Test
+ void ge_SameValues_ReturnsTrue() {
+ BigDecimal bd = new BigDecimal("10");
+ assertTrue(BigDecimals.ge(bd, bd));
+ }
+
+ @Test
+ void ge_FirstGreaterThanSecond_ReturnsTrue() {
+ BigDecimal bd1 = new BigDecimal("20");
+ BigDecimal bd2 = new BigDecimal("10");
+ assertTrue(BigDecimals.ge(bd1, bd2));
+ }
+
+ @Test
+ void lt_NullFirstValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.lt(null, bd));
+ }
+
+ @Test
+ void lt_NullSecondValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.lt(bd, null));
+ }
+
+ @Test
+ void lt_SameValues_ReturnsFalse() {
+ BigDecimal bd = new BigDecimal("10");
+ assertFalse(BigDecimals.lt(bd, bd));
+ }
+
+ @Test
+ void lt_FirstLessThanSecond_ReturnsTrue() {
+ BigDecimal bd1 = new BigDecimal("10");
+ BigDecimal bd2 = new BigDecimal("20");
+ assertTrue(BigDecimals.lt(bd1, bd2));
+ }
+
+ @Test
+ void le_NullFirstValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.le(null, bd));
+ }
+
+ @Test
+ void le_NullSecondValue_ThrowsException() {
+ BigDecimal bd = new BigDecimal("10");
+ assertThrows(NullPointerException.class, () -> BigDecimals.le(bd, null));
+ }
+
+ @Test
+ void le_SameValues_ReturnsTrue() {
+ BigDecimal bd = new BigDecimal("10");
+ assertTrue(BigDecimals.le(bd, bd));
+ }
+
+ @Test
+ void le_FirstLessThanSecond_ReturnsTrue() {
+ BigDecimal bd1 = new BigDecimal("10");
+ BigDecimal bd2 = new BigDecimal("20");
+ assertTrue(BigDecimals.le(bd1, bd2));
+ }
+
+ @Test
+ void sum_NullArray_ReturnsZero() {
+ assertEquals(BigDecimal.ZERO, BigDecimals.sum());
+ }
+
+ @Test
+ void sum_SingleNonNullValue_ReturnsSameValue() {
+ BigDecimal bd = new BigDecimal("10");
+ assertEquals(bd, BigDecimals.sum(bd));
+ }
+
+ @Test
+ void sum_SingleNullValue_ReturnsZero() {
+ assertEquals(BigDecimal.ZERO, BigDecimals.sum((BigDecimal) null));
+ }
+
+ @Test
+ void sum_MultipleValues_ReturnsCorrectSum() {
+ BigDecimal bd1 = new BigDecimal("10");
+ BigDecimal bd2 = new BigDecimal("20");
+ BigDecimal bd3 = new BigDecimal("30");
+ BigDecimal bd4 = null;
+ assertEquals(new BigDecimal("60"), BigDecimals.sum(bd1, bd2, bd3, bd4));
+ }
+
+ @Test
+ void nullToZero_NullValue_ReturnsZero() {
+ assertEquals(BigDecimal.ZERO, BigDecimals.nullToZero(null));
+ }
+
+ @Test
+ void nullToZero_NonNullValue_ReturnsSameValue() {
+ BigDecimal bd = new BigDecimal("10");
+ assertEquals(bd, BigDecimals.nullToZero(bd));
+ }
+
+ @Test
+ void of_BlankString_ReturnsZero() {
+ assertEquals(BigDecimal.ZERO, BigDecimals.of(null));
+ assertEquals(BigDecimal.ZERO, BigDecimals.of(""));
+ assertEquals(BigDecimal.ZERO, BigDecimals.of(" "));
+ }
+
+ @Test
+ void of_NonBlankString_ReturnsCorrectBigDecimal() {
+ BigDecimal bd = new BigDecimal("10");
+ assertEquals(bd, BigDecimals.of("10"));
+ }
+
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java
index 28708a7..a1796bf 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/DateTimeToolsTests.java
@@ -17,94 +17,480 @@
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.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.time.DateTimeException;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.Year;
+import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.Range;
+
+import xyz.zhouxy.plusone.commons.time.Quarter;
+import xyz.zhouxy.plusone.commons.time.YearQuarter;
+
class DateTimeToolsTests {
private static final Logger log = LoggerFactory.getLogger(DateTimeToolsTests.class);
+ // Java
+ static final LocalDateTime LOCAL_DATE_TIME = LocalDateTime.of(2024, 12, 29, 12, 58, 30, 333000000);
+ static final LocalDate LOCAL_DATE = LOCAL_DATE_TIME.toLocalDate();
+ static final LocalTime LOCAL_TIME = LOCAL_DATE_TIME.toLocalTime();
+
+ // Java - 2024-12-29 12:58:30.333333333 SystemDefaultZone
+ static final ZoneId SYS_ZONE_ID = ZoneId.systemDefault();
+ static final ZonedDateTime ZONED_DATE_TIME_WITH_SYS_ZONE = LOCAL_DATE_TIME.atZone(SYS_ZONE_ID);
+ static final Instant INSTANT_WITH_SYS_ZONE = ZONED_DATE_TIME_WITH_SYS_ZONE.toInstant();
+ static final long INSTANT_MILLIS = INSTANT_WITH_SYS_ZONE.toEpochMilli();
+
+ static final TimeZone SYS_TIME_ZONE = TimeZone.getDefault();
+ static final Date SYS_DATE = Date.from(INSTANT_WITH_SYS_ZONE);
+ static final Calendar SYS_CALENDAR = Calendar.getInstance(SYS_TIME_ZONE);
+ static {
+ SYS_CALENDAR.setTime(SYS_DATE);
+ }
+
+ // Java - 2024-12-29 12:58:30.333333333 GMT+04:00
+ static final ZoneId ZONE_ID = ZoneId.of("GMT+04:00");
+ static final ZonedDateTime ZONED_DATE_TIME = LOCAL_DATE_TIME.atZone(ZONE_ID);
+ static final Instant INSTANT = ZONED_DATE_TIME.toInstant();
+ static final long MILLIS = INSTANT.toEpochMilli();
+
+ static final TimeZone TIME_ZONE = TimeZone.getTimeZone(ZONE_ID);
+ static final Date DATE = Date.from(INSTANT);
+ static final Calendar CALENDAR = Calendar.getInstance(TIME_ZONE);
+ static {
+ CALENDAR.setTime(DATE);
+ }
+
+ // Joda
+ static final org.joda.time.LocalDateTime JODA_LOCAL_DATE_TIME
+ = new org.joda.time.LocalDateTime(2024, 12, 29, 12, 58, 30, 333);
+ static final org.joda.time.LocalDate JODA_LOCAL_DATE = JODA_LOCAL_DATE_TIME.toLocalDate();
+ static final org.joda.time.LocalTime JODA_LOCAL_TIME = JODA_LOCAL_DATE_TIME.toLocalTime();
+
+ // Joda - 2024-12-29 12:58:30.333 SystemDefaultZone
+ static final org.joda.time.DateTimeZone JODA_SYS_ZONE = org.joda.time.DateTimeZone.getDefault();
+ static final org.joda.time.DateTime JODA_DATE_TIME_WITH_SYS_ZONE = JODA_LOCAL_DATE_TIME.toDateTime(JODA_SYS_ZONE);
+ static final org.joda.time.Instant JODA_INSTANT_WITH_SYS_ZONE = JODA_DATE_TIME_WITH_SYS_ZONE.toInstant();
+ static final long JODA_INSTANT_MILLIS = JODA_INSTANT_WITH_SYS_ZONE.getMillis();
+
+ // Joda - 2024-12-29 12:58:30.333 GMT+04:00
+ static final org.joda.time.DateTimeZone JODA_ZONE = org.joda.time.DateTimeZone.forID("GMT+04:00");
+ static final org.joda.time.DateTime JODA_DATE_TIME = JODA_LOCAL_DATE_TIME.toDateTime(JODA_ZONE);
+ static final org.joda.time.Instant JODA_INSTANT = JODA_DATE_TIME.toInstant();
+ static final long JODA_MILLIS = JODA_INSTANT.getMillis();
+
+ // ================================
+ // #region - toDate
+ // ================================
+
@Test
- void testLocalNowStr() {
- String nowStr = DateTimeTools.nowStr("yyyy/MM/dd HH:mm:ss.SSS");
- log.info(nowStr);
- assertEquals(23, nowStr.length());
+ void toDate_timeMillis() {
+ assertNotEquals(SYS_DATE, DATE);
+ log.info("SYS_DATE: {}, DATE: {}", SYS_DATE, DATE);
+ assertEquals(SYS_DATE, JODA_DATE_TIME_WITH_SYS_ZONE.toDate());
+ assertEquals(SYS_DATE, DateTimeTools.toDate(INSTANT_MILLIS));
+ assertEquals(DATE, JODA_DATE_TIME.toDate());
+ assertEquals(DATE, DateTimeTools.toDate(MILLIS));
}
@Test
- void testToJoda() {
- LocalDateTime dt = LocalDateTime.of(2008, 8, 8, 20, 18, 59, 108000000);
- log.info("src: {}", dt);
- org.joda.time.LocalDateTime dt2 = DateTimeTools.toJodaLocalDateTime(dt);
- log.info("result: {}", dt2);
- org.joda.time.format.DateTimeFormatter f = org.joda.time.format.DateTimeFormat
- .forPattern("yyyy-MM-dd HH:mm:ss.SSS");
-
- assertEquals("2008-08-08 20:18:59.108", f.print(dt2));
+ void toDate_calendar() {
+ assertEquals(SYS_DATE, DateTimeTools.toDate(SYS_CALENDAR));
+ assertEquals(DATE, DateTimeTools.toDate(CALENDAR));
}
@Test
- void testToInstant() {
- ZonedDateTime dt = ZonedDateTime.of(2008, 1, 8, 10, 23, 50, 108000000, ZoneId.systemDefault());
- Instant instant = DateTimeTools.toInstant(dt.toInstant().toEpochMilli());
- String str = DateTimeTools.toString("yy-M-d HH:mm:ss.SSS", instant);
- log.info(str);
- assertEquals("08-1-8 10:23:50.108", str);
+ void toDate_instant() {
+ assertEquals(SYS_DATE, DateTimeTools.toDate(INSTANT_WITH_SYS_ZONE));
+ assertEquals(DATE, DateTimeTools.toDate(INSTANT));
}
@Test
- void testToJodaDateTime() {
- ZonedDateTime dt = ZonedDateTime.of(2008, 1, 8, 10, 23, 50, 108000000, ZoneId.systemDefault());
- Instant instant = DateTimeTools.toInstant(dt.toInstant().toEpochMilli());
-
- org.joda.time.format.DateTimeFormatter f = org.joda.time.format.DateTimeFormat
- .forPattern("yyyy-MM-dd HH:mm:ss.SSS");
-
- org.joda.time.DateTime jodaDateTime = DateTimeTools.toJodaDateTime(instant, ZoneId.of("+08:00"));
- log.info("jodaDateTime: {}", jodaDateTime);
- assertEquals("2008-01-08 10:23:50.108", f.print(jodaDateTime));
-
- jodaDateTime = DateTimeTools.toJodaDateTime(instant, ZoneId.of("+02:00"));
- log.info("jodaDateTime: {}", jodaDateTime);
- assertEquals("2008-01-08 04:23:50.108", f.print(jodaDateTime));
+ void toDate_ZoneDateTime() {
+ assertEquals(SYS_DATE, DateTimeTools.toDate(ZONED_DATE_TIME_WITH_SYS_ZONE));
+ assertEquals(DATE, DateTimeTools.toDate(ZONED_DATE_TIME));
}
@Test
- void test() {
- java.time.Instant now = java.time.Instant.now();
- org.joda.time.DateTime jodaDateTime = DateTimeTools.toJodaDateTime(now, ZoneId.of("America/New_York"));
- org.joda.time.format.DateTimeFormatter formatter = org.joda.time.format.DateTimeFormat
- .forPattern("yyyy-MM-dd HH:mm:ss.SSS");
- log.info(formatter.print(jodaDateTime));
- log.info(jodaDateTime.getZone().toString());
- log.info(jodaDateTime.toString());
- log.info("==========================================");
- org.joda.time.Instant instant = new org.joda.time.Instant(System.currentTimeMillis() - 500000);
- log.info(instant.toString());
- log.info(DateTimeTools.toJavaInstant(instant).toString());
- log.info(DateTimeTools.toZonedDateTime(instant, org.joda.time.DateTimeZone.forID("America/New_York"))
- .toString());
+ void toDate_LocalDateTimeAndZoneId() {
+ assertEquals(SYS_DATE, DateTimeTools.toDate(LOCAL_DATE_TIME, SYS_ZONE_ID));
+ assertEquals(DATE, DateTimeTools.toDate(LOCAL_DATE_TIME, ZONE_ID));
+ assertEquals(SYS_DATE, DateTimeTools.toDate(LOCAL_DATE, LOCAL_TIME, SYS_ZONE_ID));
+ assertEquals(DATE, DateTimeTools.toDate(LOCAL_DATE, LOCAL_TIME, ZONE_ID));
+ }
+
+ // ================================
+ // #endregion - toDate
+ // ================================
+
+ // ================================
+ // #region - toInstant
+ // ================================
+
+ @Test
+ void toInstant_timeMillis() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(INSTANT_MILLIS));
+ assertEquals(INSTANT, DateTimeTools.toInstant(MILLIS));
}
@Test
- void testToJodaInstant() {
- java.time.Instant javaInstant = java.time.Instant.now();
- log.info("javaInstant: {}", javaInstant);
-
- org.joda.time.Instant jodaInstant = DateTimeTools.toJodaInstant(javaInstant);
- log.info("jodaInstant: {}", jodaInstant);
-
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
- DateTimeFormatter formatter2 = formatter.withZone(ZoneId.systemDefault());
- log.info("{}", formatter);
- log.info("{}", formatter2);
+ void toInstant_Date() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(SYS_DATE));
+ assertEquals(INSTANT, DateTimeTools.toInstant(DATE));
}
+
+ @Test
+ void toInstant_Calendar() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(SYS_CALENDAR));
+ assertEquals(INSTANT, DateTimeTools.toInstant(CALENDAR));
+ }
+
+ @Test
+ void toInstant_ZonedDateTime() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(ZONED_DATE_TIME_WITH_SYS_ZONE));
+ assertEquals(INSTANT, DateTimeTools.toInstant(ZONED_DATE_TIME));
+ }
+
+ @Test
+ void toInstant_LocalDateTimeAndZone() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toInstant(LOCAL_DATE_TIME, SYS_ZONE_ID));
+ assertEquals(INSTANT, DateTimeTools.toInstant(LOCAL_DATE_TIME, ZONE_ID));
+ }
+
+ // ================================
+ // #endregion - toInstant
+ // ================================
+
+ // ================================
+ // #region - toZonedDateTime
+ // ================================
+
+ @Test
+ void toZonedDateTime_TimeMillisAndZone() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(INSTANT_MILLIS, SYS_ZONE_ID));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(MILLIS, ZONE_ID));
+ }
+
+ @Test
+ void toZonedDateTime_DateAndZoneId() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_DATE, SYS_ZONE_ID));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(DATE, ZONE_ID));
+ }
+
+ @Test
+ void toZonedDateTime_DateAndTimeZone() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_DATE, SYS_TIME_ZONE));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(DATE, TIME_ZONE));
+ }
+
+ @Test
+ void toZonedDateTime_Calendar() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_CALENDAR));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(CALENDAR));
+ }
+
+ @Test
+ void toZonedDateTime_CalendarAndZoneId() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_CALENDAR, SYS_ZONE_ID));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(CALENDAR, ZONE_ID));
+ }
+
+ @Test
+ void toZonedDateTime_CalendarAndTimeZone() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(SYS_CALENDAR, SYS_TIME_ZONE));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(CALENDAR, TIME_ZONE));
+ }
+
+ @Test
+ void toZonedDateTime_LocalDateTimeAndZoneId() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(LOCAL_DATE_TIME, SYS_ZONE_ID));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(LOCAL_DATE_TIME, ZONE_ID));
+ }
+
+ // ================================
+ // #endregion - toZonedDateTime
+ // ================================
+
+ // ================================
+ // #region - toLocalDateTime
+ // ================================
+
+ @Test
+ void toLocalDateTime_TimeMillisAndZoneId() {
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(INSTANT_MILLIS, SYS_ZONE_ID));
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(MILLIS, ZONE_ID));
+ }
+
+ @Test
+ void toLocalDateTime_DateAndZoneId() {
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_DATE, SYS_ZONE_ID));
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(DATE, ZONE_ID));
+ }
+
+ @Test
+ void toLocalDateTime_DateAndTimeZone() {
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_DATE, SYS_TIME_ZONE));
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(DATE, TIME_ZONE));
+ }
+
+ @Test
+ void toLocalDateTime_CalendarAndZoneId() {
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_CALENDAR, SYS_ZONE_ID));
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(CALENDAR, ZONE_ID));
+ }
+
+ @Test
+ void toLocalDateTime_CalendarAndTimeZone() {
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(SYS_CALENDAR, SYS_TIME_ZONE));
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(CALENDAR, TIME_ZONE));
+ }
+
+ @Test
+ void toLocalDateTime_ZonedDateTimeAndZoneId() {
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(ZONED_DATE_TIME_WITH_SYS_ZONE, SYS_ZONE_ID));
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toLocalDateTime(ZONED_DATE_TIME, ZONE_ID));
+ }
+
+ // ================================
+ // #endregion - toLocalDateTime
+ // ================================
+
+ // ================================
+ // #region - toJodaInstant
+ // ================================
+
+ @Test
+ void toJodaInstant_JavaInstant() {
+ assertEquals(JODA_INSTANT_WITH_SYS_ZONE, DateTimeTools.toJodaInstant(INSTANT_WITH_SYS_ZONE));
+ assertEquals(JODA_INSTANT, DateTimeTools.toJodaInstant(INSTANT));
+ }
+
+ @Test
+ void toJodaInstant_ZonedDateTime() {
+ assertEquals(JODA_INSTANT_WITH_SYS_ZONE, DateTimeTools.toJodaInstant(ZONED_DATE_TIME_WITH_SYS_ZONE));
+ assertEquals(JODA_INSTANT, DateTimeTools.toJodaInstant(ZONED_DATE_TIME));
+ }
+
+ @Test
+ void toJodaInstant_LocalDateTimeAndZoneId() {
+ assertEquals(JODA_INSTANT_WITH_SYS_ZONE, DateTimeTools.toJodaInstant(LOCAL_DATE_TIME, SYS_ZONE_ID));
+ assertEquals(JODA_INSTANT, DateTimeTools.toJodaInstant(LOCAL_DATE_TIME, ZONE_ID));
+ }
+
+ // ================================
+ // #endregion - toJodaInstant
+ // ================================
+
+ // ================================
+ // #region - toJavaInstant
+ // ================================
+
+ @Test
+ void toJavaInstant_JodaInstant() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toJavaInstant(JODA_INSTANT_WITH_SYS_ZONE));
+ assertEquals(INSTANT, DateTimeTools.toJavaInstant(JODA_INSTANT));
+ }
+
+ @Test
+ void toJavaInstant_JodaDateTime() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toJavaInstant(JODA_DATE_TIME_WITH_SYS_ZONE));
+ assertEquals(INSTANT, DateTimeTools.toJavaInstant(JODA_DATE_TIME));
+ }
+
+ @Test
+ void toJavaInstant_JodaLocalDateTimeAndJodaDateTimeZone() {
+ assertEquals(INSTANT_WITH_SYS_ZONE, DateTimeTools.toJavaInstant(JODA_LOCAL_DATE_TIME, JODA_SYS_ZONE));
+ assertEquals(INSTANT, DateTimeTools.toJavaInstant(JODA_LOCAL_DATE_TIME, JODA_ZONE));
+ }
+
+ // ================================
+ // #endregion - toJavaInstant
+ // ================================
+
+ // ================================
+ // #region - toJodaDateTime
+ // ================================
+
+ @Test
+ void toJodaDateTime_ZonedDateTime() {
+ assertEquals(JODA_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toJodaDateTime(ZONED_DATE_TIME_WITH_SYS_ZONE));
+ assertEquals(JODA_DATE_TIME, DateTimeTools.toJodaDateTime(ZONED_DATE_TIME));
+ }
+
+ @Test
+ void toJodaDateTime_LocalDateTimeAndZoneId() {
+ assertEquals(JODA_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toJodaDateTime(LOCAL_DATE_TIME, SYS_ZONE_ID));
+ assertEquals(JODA_DATE_TIME, DateTimeTools.toJodaDateTime(LOCAL_DATE_TIME, ZONE_ID));
+ }
+
+ @Test
+ void toJodaDateTime_InstantAndZoneId() {
+ assertEquals(JODA_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toJodaDateTime(INSTANT_WITH_SYS_ZONE, SYS_ZONE_ID));
+ assertEquals(JODA_DATE_TIME, DateTimeTools.toJodaDateTime(INSTANT, ZONE_ID));
+ }
+
+ // ================================
+ // #endregion - toJodaDateTime
+ // ================================
+
+ // ================================
+ // #region - toZonedDateTime
+ // ================================
+
+ @Test
+ void toZonedDateTime_JodaDateTime() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(JODA_DATE_TIME_WITH_SYS_ZONE));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(JODA_DATE_TIME));
+ }
+
+ @Test
+ void toZonedDateTime_JodaLocalDateTimeAndJodaDateTimeZone() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(JODA_LOCAL_DATE_TIME, JODA_SYS_ZONE));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(JODA_LOCAL_DATE_TIME, JODA_ZONE));
+ }
+
+ @Test
+ void toZonedDateTime_JodaInstantAndJodaDateTimeZone() {
+ assertEquals(ZONED_DATE_TIME_WITH_SYS_ZONE, DateTimeTools.toZonedDateTime(JODA_INSTANT_WITH_SYS_ZONE, JODA_SYS_ZONE));
+ assertEquals(ZONED_DATE_TIME, DateTimeTools.toZonedDateTime(JODA_INSTANT, JODA_ZONE));
+ }
+
+ // ================================
+ // #endregion - toZonedDateTime
+ // ================================
+
+ // ================================
+ // #region - toJodaLocalDateTime
+ // ================================
+
+ @Test
+ void toJodaLocalDateTime_JavaLocalDateTime() {
+ assertEquals(JODA_LOCAL_DATE_TIME, DateTimeTools.toJodaLocalDateTime(LOCAL_DATE_TIME));
+ }
+
+ @Test
+ void toJavaLocalDateTime_JodaLocalDateTime() {
+ assertEquals(LOCAL_DATE_TIME, DateTimeTools.toJavaLocalDateTime(JODA_LOCAL_DATE_TIME));
+ }
+
+ // ================================
+ // #endregion - toJodaLocalDateTime
+ // ================================
+
+ // ================================
+ // #region - ZondId <--> DateTimeZone
+ // ================================
+
+ @Test
+ void convertJavaZoneIdAndJodaDateTimeZone() {
+ assertEquals(SYS_ZONE_ID, DateTimeTools.toJavaZone(JODA_SYS_ZONE));
+ assertEquals(ZONE_ID, DateTimeTools.toJavaZone(JODA_ZONE));
+ assertEquals(JODA_SYS_ZONE, DateTimeTools.toJodaZone(SYS_ZONE_ID));
+ assertEquals(JODA_ZONE, DateTimeTools.toJodaZone(ZONE_ID));
+ }
+
+ // ================================
+ // #endregion - ZondId <--> DateTimeZone
+ // ================================
+
+ // ================================
+ // #region - YearQuarter & Quarter
+ // ================================
+
+ @Test
+ void getQuarter() {
+ YearQuarter expectedYearQuarter = YearQuarter.of(2024, 4);
+ assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(SYS_DATE));
+ assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(SYS_CALENDAR));
+ assertEquals(Quarter.Q4, DateTimeTools.getQuarter(Month.DECEMBER));
+ assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(2024, Month.DECEMBER));
+ assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(YearMonth.of(2024, Month.DECEMBER)));
+ assertEquals(expectedYearQuarter, DateTimeTools.getQuarter(LOCAL_DATE));
+ }
+
+ // ================================
+ // #endregion - YearQuarter & Quarter
+ // ================================
+
+ // ================================
+ // #region - others
+ // ================================
+
+ @Test
+ void startDateOfYear() {
+ assertEquals(LocalDate.of(2008, 1, 1), DateTimeTools.startDateOfYear(2008));
+ assertEquals(LocalDate.of(2008, 12, 31), DateTimeTools.endDateOfYear(2008));
+ 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));
+
+ Range localDateTimeRange = DateTimeTools.toDateTimeRange(LOCAL_DATE);
+ assertEquals(LOCAL_DATE.atStartOfDay(), localDateTimeRange.lowerEndpoint());
+ 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 zonedDateTimeRange = DateTimeTools.toDateTimeRange(LOCAL_DATE, SYS_ZONE_ID);
+ assertEquals(LOCAL_DATE.atStartOfDay().atZone(SYS_ZONE_ID), zonedDateTimeRange.lowerEndpoint());
+ 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)));
+ }
+
+ // ================================
+ // #endregion - others
+ // ================================
+
+ // ================================
+ // #region - toString
+ // ================================
+
+ @Test
+ void testToString() {
+ assertEquals("2024", DateTimeTools.toYearString(2024));
+ assertEquals("999999999", DateTimeTools.toYearString(Year.MAX_VALUE));
+ assertEquals("-999999999", DateTimeTools.toYearString(Year.MIN_VALUE));
+ assertThrows(DateTimeException.class, () -> DateTimeTools.toYearString(Year.MIN_VALUE - 1));
+ assertThrows(DateTimeException.class, () -> DateTimeTools.toYearString(Year.MAX_VALUE + 1));
+
+ assertEquals("01", DateTimeTools.toMonthStringMM(1));
+ assertEquals("02", DateTimeTools.toMonthStringMM(2));
+ assertEquals("3", DateTimeTools.toMonthStringM(3));
+ assertEquals("04", DateTimeTools.toMonthStringMM(Month.APRIL));
+ assertEquals("05", DateTimeTools.toMonthStringMM(Month.MAY));
+ assertEquals("6", DateTimeTools.toMonthStringM(Month.JUNE));
+
+ assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringM(0));
+ assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringMM(0));
+ assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringM(13));
+ assertThrows(DateTimeException.class, () -> DateTimeTools.toMonthStringMM(13));
+ }
+
+ // ================================
+ // #endregion - toString
+ // ================================
}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/EnumToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/EnumToolsTests.java
new file mode 100644
index 0000000..d4442cd
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/EnumToolsTests.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2024 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.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+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 org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+@SuppressWarnings("deprecation")
+public
+class EnumToolsTests {
+
+ private enum MyEnum {
+ VALUE_0,
+ VALUE_1,
+ VALUE_2,
+ VALUE_3,
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { 0, 1, 2, 3 })
+ void testCheckOrdinal_success(int ordinal) {
+ assertEquals(ordinal, EnumTools.checkOrdinal(MyEnum.class, ordinal));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 4 })
+ void testCheckOrdinal_EnumConstantNotPresentException(int ordinal) {
+ assertThrows(EnumConstantNotPresentException.class,
+ () -> EnumTools.checkOrdinal(MyEnum.class, ordinal));
+ }
+
+ @Test
+ void testCheckOrdinal_null() {
+ assertThrows(NullPointerException.class,
+ () -> EnumTools.checkOrdinal(MyEnum.class, null));
+ assertThrows(NullPointerException.class,
+ () -> EnumTools.checkOrdinal(null, 0));
+ assertThrows(NullPointerException.class,
+ () -> EnumTools.checkOrdinal(null, null));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { 0, 1, 2, 3 })
+ void testCheckOrdinalNullable_success(int ordinal) {
+ assertEquals(ordinal, EnumTools.checkOrdinalNullable(MyEnum.class, ordinal));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 4 })
+ void testCheckOrdinalNullable_EnumConstantNotPresentException(int ordinal) {
+ assertThrows(EnumConstantNotPresentException.class, () -> {
+ EnumTools.checkOrdinalNullable(MyEnum.class, ordinal);
+ });
+ }
+
+ @Test
+ void testCheckOrdinalNullable_null() {
+ assertNull(EnumTools.checkOrdinalNullable(MyEnum.class, null));
+
+ assertThrows(NullPointerException.class, () -> {
+ EnumTools.checkOrdinalNullable(null, 0);
+ });
+
+ assertThrows(NullPointerException.class, () -> {
+ EnumTools.checkOrdinalNullable(null, null);
+ });
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { 0, 1, 2, 3 })
+ void testCheckOrdinalOrDefault_0To3(int ordinal) {
+ assertEquals(ordinal, EnumTools.checkOrdinalOrDefault(MyEnum.class, ordinal));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 4 })
+ void testCheckOrdinalOrDefault_EnumConstantNotPresentException(int ordinal) {
+ assertThrows(EnumConstantNotPresentException.class, () -> {
+ EnumTools.checkOrdinalOrDefault(MyEnum.class, ordinal);
+ });
+ assertThrows(EnumConstantNotPresentException.class, () -> {
+ EnumTools.checkOrdinalOrDefault(MyEnum.class, null, ordinal);
+ });
+ }
+
+ @Test
+ void testCheckOrdinalOrDefault_null() {
+ assertEquals(0, EnumTools.checkOrdinalOrDefault(MyEnum.class, null));
+ assertEquals(1, EnumTools.checkOrdinalOrDefault(MyEnum.class, null, 1));
+ assertNull(EnumTools.checkOrdinalOrDefault(MyEnum.class, null, null));
+ }
+
+ @Test
+ void testGetValueNullable_0To3() {
+ assertSame(MyEnum.VALUE_0, EnumTools.getValueNullable(MyEnum.class, 0));
+ assertSame(MyEnum.VALUE_1, EnumTools.getValueNullable(MyEnum.class, 1));
+ assertSame(MyEnum.VALUE_2, EnumTools.getValueNullable(MyEnum.class, 2));
+ assertSame(MyEnum.VALUE_3, EnumTools.getValueNullable(MyEnum.class, 3));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 4 })
+ void testGetValueNullable_EnumConstantNotPresentException(int ordinal) {
+ assertThrows(EnumConstantNotPresentException.class, () -> {
+ EnumTools.getValueNullable(MyEnum.class, ordinal);
+ });
+ }
+
+ @Test
+ void testGetValueNullable_null() {
+ assertNull(EnumTools.getValueNullable(MyEnum.class, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueNullable(null, 0));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueNullable(null, null));
+ }
+
+ @Test
+ void testGetValueOrDefault_0To3() {
+ assertSame(MyEnum.VALUE_0, EnumTools.getValueOrDefault(MyEnum.class, 0));
+ assertSame(MyEnum.VALUE_1, EnumTools.getValueOrDefault(MyEnum.class, 1));
+ assertSame(MyEnum.VALUE_2, EnumTools.getValueOrDefault(MyEnum.class, 2));
+ assertSame(MyEnum.VALUE_3, EnumTools.getValueOrDefault(MyEnum.class, 3));
+
+ assertSame(MyEnum.VALUE_0, EnumTools.getValueOrDefault(MyEnum.class, 0, () -> MyEnum.VALUE_1));
+ assertSame(MyEnum.VALUE_1, EnumTools.getValueOrDefault(MyEnum.class, 1, () -> MyEnum.VALUE_0));
+ assertSame(MyEnum.VALUE_2, EnumTools.getValueOrDefault(MyEnum.class, 2, () -> MyEnum.VALUE_0));
+ assertSame(MyEnum.VALUE_3, EnumTools.getValueOrDefault(MyEnum.class, 3, () -> MyEnum.VALUE_0));
+
+ assertSame(MyEnum.VALUE_0, EnumTools.getValueOrDefault(MyEnum.class, 0, () -> null));
+ assertSame(MyEnum.VALUE_1, EnumTools.getValueOrDefault(MyEnum.class, 1, () -> null));
+ assertSame(MyEnum.VALUE_2, EnumTools.getValueOrDefault(MyEnum.class, 2, () -> null));
+ assertSame(MyEnum.VALUE_3, EnumTools.getValueOrDefault(MyEnum.class, 3, () -> null));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 4 })
+ void testGetValueOrDefault_EnumConstantNotPresentException(int ordinal) {
+ assertThrows(EnumConstantNotPresentException.class, () -> {
+ EnumTools.getValueOrDefault(MyEnum.class, ordinal);
+ });
+ assertThrows(EnumConstantNotPresentException.class, () -> {
+ EnumTools.getValueOrDefault(MyEnum.class, ordinal, () -> MyEnum.VALUE_0);
+ });
+ }
+
+ @Test
+ void testGetValueOrDefault_null() {
+ assertSame(MyEnum.VALUE_0, EnumTools.getValueOrDefault(MyEnum.class, null));
+ assertSame(MyEnum.VALUE_0, EnumTools.getValueOrDefault(MyEnum.class, null, () -> MyEnum.VALUE_0));
+ assertNull(EnumTools.getValueOrDefault(MyEnum.class, null, () -> null));
+
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, -1));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, 0));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, 4));
+
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, null, () -> MyEnum.VALUE_0));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, -1, () -> MyEnum.VALUE_1));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, 0, () -> MyEnum.VALUE_1));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, 4, () -> MyEnum.VALUE_0));
+
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(MyEnum.class, null, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(MyEnum.class, -1, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(MyEnum.class, 0, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(MyEnum.class, 4, null));
+
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, null, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, -1, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, 0, null));
+ assertThrows(NullPointerException.class, () -> EnumTools.getValueOrDefault(null, 4, null));
+
+ }
+
+ @Test
+ void testValueOf_0To3() {
+ assertSame(MyEnum.VALUE_0, EnumTools.valueOf(MyEnum.class, 0));
+ assertSame(MyEnum.VALUE_1, EnumTools.valueOf(MyEnum.class, 1));
+ assertSame(MyEnum.VALUE_2, EnumTools.valueOf(MyEnum.class, 2));
+ assertSame(MyEnum.VALUE_3, EnumTools.valueOf(MyEnum.class, 3));
+
+ assertSame(MyEnum.VALUE_0, EnumTools.valueOf(MyEnum.class, 0, MyEnum.VALUE_1));
+ assertSame(MyEnum.VALUE_1, EnumTools.valueOf(MyEnum.class, 1, MyEnum.VALUE_0));
+ assertSame(MyEnum.VALUE_2, EnumTools.valueOf(MyEnum.class, 2, MyEnum.VALUE_0));
+ assertSame(MyEnum.VALUE_3, EnumTools.valueOf(MyEnum.class, 3, MyEnum.VALUE_0));
+
+ assertSame(MyEnum.VALUE_0, EnumTools.valueOf(MyEnum.class, 0, null));
+ assertSame(MyEnum.VALUE_1, EnumTools.valueOf(MyEnum.class, 1, null));
+ assertSame(MyEnum.VALUE_2, EnumTools.valueOf(MyEnum.class, 2, null));
+ assertSame(MyEnum.VALUE_3, EnumTools.valueOf(MyEnum.class, 3, null));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { -1, 4 })
+ void testValueOf_EnumConstantNotPresentException(int ordinal) {
+ assertThrows(EnumConstantNotPresentException.class, () -> EnumTools.valueOf(MyEnum.class, ordinal));
+ assertThrows(EnumConstantNotPresentException.class, () -> EnumTools.valueOf(MyEnum.class, ordinal, MyEnum.VALUE_0));
+ assertThrows(EnumConstantNotPresentException.class, () -> EnumTools.valueOf(MyEnum.class, ordinal, null));
+ }
+
+ @Test
+ void testValueOf_null() {
+ assertThrows(NullPointerException.class, () -> EnumTools.valueOf(null, 0));
+ assertSame(MyEnum.VALUE_0, EnumTools.valueOf(MyEnum.class, null, MyEnum.VALUE_0));
+ assertNull(EnumTools.valueOf(MyEnum.class, null, null));
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java
new file mode 100644
index 0000000..ccc3f59
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 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.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import cn.hutool.core.collection.ConcurrentHashSet;
+
+public class IdGeneratorTests {
+
+ final ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue());
+
+ @Test
+ void testSnowflakeIdGenerator() { // NOSONAR
+ final SnowflakeIdGenerator snowflake = new SnowflakeIdGenerator(0, 0);
+ final Set ids = new ConcurrentHashSet<>();
+ for (int i = 0; i < 10000; i++) {
+ executor.execute(() -> {
+ for (int j = 0; j < 50000; j++) {
+ if (false == ids.add(snowflake.nextId())) {
+ throw new RuntimeException("重复ID!");
+ }
+ }
+ });
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(longs = { 0L, 1L, 108L, 300L })
+ void testIdWorker(long workerId) { // NOSONAR
+ // 如果使用 new IdWorker(0L) 创建,会和下面的 IdGenerator#nextSnowflakeId 使用相同 workerId 的不同 IdWorker 实例,造成 ID 重复
+ final IdWorker idWorker = IdGenerator.getSnowflakeIdGenerator(workerId);
+
+ final Set ids = new ConcurrentHashSet<>();
+ for (int i = 0; i < 10000; i++) {
+ executor.execute(() -> {
+ for (int j = 0; j < 50000; j++) {
+ if (false == ids.add(idWorker.nextId())) {
+ throw new RuntimeException("重复ID!");
+ }
+ }
+ });
+ executor.execute(() -> {
+ for (int j = 0; j < 50000; j++) {
+ if (false == ids.add(IdGenerator.nextSnowflakeId(workerId))) {
+ throw new RuntimeException("重复ID!");
+ }
+ }
+ });
+ }
+ }
+
+ @Test
+ void testToSimpleString() {
+ UUID id = UUID.randomUUID();
+ assertEquals(id.toString().replaceAll("-", ""),
+ IdGenerator.toSimpleString(id));
+ }
+
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/MyBatisSqlBuilderTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/MyBatisSqlBuilderTests.java
deleted file mode 100644
index 419914a..0000000
--- a/src/test/java/xyz/zhouxy/plusone/commons/util/MyBatisSqlBuilderTests.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2023-2024 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.util;
-
-import static xyz.zhouxy.plusone.commons.sql.MyBatisSql.IN;
-
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import xyz.zhouxy.plusone.commons.sql.MyBatisSql;
-
-class MyBatisSqlBuilderTests {
- private static final Logger log = LoggerFactory.getLogger(MyBatisSqlBuilderTests.class);
-
- @Test
- void test() {
- // List ids = Arrays.asList("2333", "4501477");
- MyBatisSql sql = MyBatisSql.newScriptSql()
- .SELECT("*")
- .FROM("test_table")
- .WHERE(IN("id", "ids"));
- log.info("sql: {}", sql);
- }
-}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/NumbersTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/NumbersTests.java
new file mode 100644
index 0000000..1e2435f
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/NumbersTests.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2024 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.util;
+
+import org.junit.jupiter.api.Test;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public
+class NumbersTests {
+
+ @Test
+ public void sum_ShortArray_ReturnsCorrectSum() {
+ short[] numbers = {1, 2, 3, 4};
+ int result = Numbers.sum(numbers);
+ assertEquals(10, result);
+ }
+
+ @Test
+ public void sum_IntArray_ReturnsCorrectSum() {
+ int[] numbers = {1, 2, 3, 4};
+ long result = Numbers.sum(numbers);
+ assertEquals(10L, result);
+ }
+
+ @Test
+ public void sum_LongArray_ReturnsCorrectSum() {
+ long[] numbers = {1, 2, 3, 4};
+ long result = Numbers.sum(numbers);
+ assertEquals(10L, result);
+ }
+
+ @Test
+ public void sum_FloatArray_ReturnsCorrectSum() {
+ float[] numbers = {1.5f, 2.5f, 3.5f};
+ double result = Numbers.sum(numbers);
+ assertEquals(7.5, result);
+ }
+
+ @Test
+ public void sum_DoubleArray_ReturnsCorrectSum() {
+ double[] numbers = {1.5, 2.5, 3.5};
+ double result = Numbers.sum(numbers);
+ assertEquals(7.5, result);
+ }
+
+ @Test
+ public void sum_BigIntegerArray_ReturnsCorrectSum() {
+ BigInteger[] numbers = {new BigInteger("1"), new BigInteger("2"), new BigInteger("3")};
+ BigInteger result = Numbers.sum(numbers);
+ assertEquals(new BigInteger("6"), result);
+ }
+
+ @Test
+ public void sum_BigDecimalArray_ReturnsCorrectSum() {
+ BigDecimal[] numbers = {new BigDecimal("1.5"), new BigDecimal("2.5"), new BigDecimal("3.5")};
+ BigDecimal result = Numbers.sum(numbers);
+ assertEquals(new BigDecimal("7.5"), result);
+ }
+
+ @Test
+ public void nullToZero_ByteNotNull_ReturnsSameValue() {
+ Byte value = 5;
+ byte result = Numbers.nullToZero(value);
+ assertEquals(5, result);
+ }
+
+ @Test
+ public void nullToZero_ByteNull_ReturnsZero() {
+ Byte value = null;
+ byte result = Numbers.nullToZero(value);
+ assertEquals(0, result);
+ }
+
+ @Test
+ public void nullToZero_ShortNotNull_ReturnsSameValue() {
+ Short value = 5;
+ short result = Numbers.nullToZero(value);
+ assertEquals(5, result);
+ }
+
+ @Test
+ public void nullToZero_ShortNull_ReturnsZero() {
+ Short value = null;
+ short result = Numbers.nullToZero(value);
+ assertEquals(0, result);
+ }
+
+ @Test
+ public void nullToZero_IntegerNotNull_ReturnsSameValue() {
+ Integer value = 5;
+ int result = Numbers.nullToZero(value);
+ assertEquals(5, result);
+ }
+
+ @Test
+ public void nullToZero_IntegerNull_ReturnsZero() {
+ Integer value = null;
+ int result = Numbers.nullToZero(value);
+ assertEquals(0, result);
+ }
+
+ @Test
+ public void nullToZero_LongNotNull_ReturnsSameValue() {
+ Long value = 5L;
+ long result = Numbers.nullToZero(value);
+ assertEquals(5L, result);
+ }
+
+ @Test
+ public void nullToZero_LongNull_ReturnsZero() {
+ Long value = null;
+ long result = Numbers.nullToZero(value);
+ assertEquals(0L, result);
+ }
+
+ @Test
+ public void nullToZero_FloatNotNull_ReturnsSameValue() {
+ Float value = 5.0F;
+ float result = Numbers.nullToZero(value);
+ assertEquals(5.0F, result);
+ }
+
+ @Test
+ public void nullToZero_FloatNull_ReturnsZero() {
+ Float value = null;
+ float result = Numbers.nullToZero(value);
+ assertEquals(0.0F, result);
+ }
+
+ @Test
+ public void nullToZero_DoubleNotNull_ReturnsSameValue() {
+ Double value = 5.0;
+ double result = Numbers.nullToZero(value);
+ assertEquals(5.0, result);
+ }
+
+ @Test
+ public void nullToZero_DoubleNull_ReturnsZero() {
+ Double value = null;
+ double result = Numbers.nullToZero(value);
+ assertEquals(0.0, result);
+ }
+
+ @Test
+ public void nullToZero_BigIntegerNotNull_ReturnsSameValue() {
+ BigInteger value = new BigInteger("5");
+ BigInteger result = Numbers.nullToZero(value);
+ assertEquals(new BigInteger("5"), result);
+ }
+
+ @Test
+ public void nullToZero_BigIntegerNull_ReturnsZero() {
+ BigInteger value = null;
+ BigInteger result = Numbers.nullToZero(value);
+ assertEquals(BigInteger.ZERO, result);
+ }
+
+ @Test
+ public void nullToZero_BigDecimalNotNull_ReturnsSameValue() {
+ BigDecimal value = new BigDecimal("5.0");
+ BigDecimal result = Numbers.nullToZero(value);
+ assertEquals(new BigDecimal("5.0"), result);
+ }
+
+ @Test
+ public void nullToZero_BigDecimalNull_ReturnsZero() {
+ BigDecimal value = null;
+ BigDecimal result = Numbers.nullToZero(value);
+ assertEquals(BigDecimal.ZERO, result);
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/OptionalToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/OptionalToolsTests.java
new file mode 100644
index 0000000..79199e4
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/OptionalToolsTests.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2024 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.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+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.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * {@link OptionalTools} 单元测试
+ */
+public
+class OptionalToolsTests {
+
+ @Test
+ void optionalOf_NullInteger_ReturnsEmptyOptionalInt() {
+ OptionalInt result = OptionalTools.optionalOf((Integer) null);
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ void optionalOf_ValidInteger_ReturnsOptionalIntWithValue() {
+ OptionalInt result = OptionalTools.optionalOf(10);
+ assertTrue(result.isPresent());
+ assertEquals(10, result.getAsInt());
+ }
+
+ @Test
+ void toOptionalInt_NullOptionalInteger_ReturnsEmptyOptionalInt() {
+ OptionalInt result = OptionalTools.toOptionalInt(Optional.ofNullable(null));
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ void toOptionalInt_ValidOptionalInteger_ReturnsOptionalIntWithValue() {
+ OptionalInt result = OptionalTools.toOptionalInt(Optional.of(10));
+ assertTrue(result.isPresent());
+ assertEquals(10, result.getAsInt());
+ }
+
+ @Test
+ void optionalOf_NullLong_ReturnsEmptyOptionalLong() {
+ OptionalLong result = OptionalTools.optionalOf((Long) null);
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ void optionalOf_ValidLong_ReturnsOptionalLongWithValue() {
+ OptionalLong result = OptionalTools.optionalOf(10L);
+ assertTrue(result.isPresent());
+ assertEquals(10L, result.getAsLong());
+ }
+
+ @Test
+ void toOptionalLong_NullOptionalLong_ReturnsEmptyOptionalLong() {
+ OptionalLong result = OptionalTools.toOptionalLong(Optional.ofNullable(null));
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ void toOptionalLong_ValidOptionalLong_ReturnsOptionalLongWithValue() {
+ OptionalLong result = OptionalTools.toOptionalLong(Optional.of(10L));
+ assertTrue(result.isPresent());
+ assertEquals(10L, result.getAsLong());
+ }
+
+ @Test
+ void optionalOf_NullDouble_ReturnsEmptyOptionalDouble() {
+ OptionalDouble result = OptionalTools.optionalOf((Double) null);
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ void optionalOf_ValidDouble_ReturnsOptionalDoubleWithValue() {
+ OptionalDouble result = OptionalTools.optionalOf(10.0);
+ assertTrue(result.isPresent());
+ assertEquals(10.0, result.getAsDouble(), 0.0001);
+ }
+
+ @Test
+ void toOptionalDouble_NullOptionalDouble_ReturnsEmptyOptionalDouble() {
+ OptionalDouble result = OptionalTools.toOptionalDouble(Optional.ofNullable(null));
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ void toOptionalDouble_ValidOptionalDouble_ReturnsOptionalDoubleWithValue() {
+ OptionalDouble result = OptionalTools.toOptionalDouble(Optional.of(10.0));
+ assertTrue(result.isPresent());
+ assertEquals(10.0, result.getAsDouble(), 0.0001);
+ }
+
+ @Test
+ void orElseNull_NullOptional_ThrowsNullPointerException() {
+ assertThrows(NullPointerException.class,
+ () -> OptionalTools.orElseNull(null));
+ }
+
+ @Test
+ void orElseNull_EmptyOptional_ReturnsNull() {
+ Object result = OptionalTools.orElseNull(Optional.empty());
+ assertNull(result);
+ }
+
+ @Test
+ void orElseNull_PresentOptional_ReturnsValue() {
+ Object result = OptionalTools.orElseNull(Optional.of("test"));
+ assertEquals("test", result);
+ }
+
+ @Test
+ void toInteger_NullOptionalInt_ThrowsNullPointerException() {
+ assertThrows(NullPointerException.class,
+ () -> OptionalTools.toInteger(null));
+ }
+
+ @Test
+ void toInteger_EmptyOptionalInt_ReturnsNull() {
+ Integer result = OptionalTools.toInteger(OptionalInt.empty());
+ assertNull(result);
+ }
+
+ @Test
+ void toInteger_PresentOptionalInt_ReturnsValue() {
+ Integer result = OptionalTools.toInteger(OptionalInt.of(10));
+ assertEquals(10, result);
+ }
+
+ @Test
+ void toLong_NullOptionalLong_ThrowsNullPointerException() {
+ assertThrows(NullPointerException.class,
+ () -> OptionalTools.toLong(null));
+ }
+
+ @Test
+ void toLong_EmptyOptionalLong_ReturnsNull() {
+ Long result = OptionalTools.toLong(OptionalLong.empty());
+ assertNull(result);
+ }
+
+ @Test
+ void toLong_PresentOptionalLong_ReturnsValue() {
+ Long result = OptionalTools.toLong(OptionalLong.of(10L));
+ assertEquals(10L, result);
+ }
+
+ @Test
+ void toDouble_NullOptionalDouble_ThrowsNullPointerException() {
+ assertThrows(NullPointerException.class,
+ () -> OptionalTools.toDouble(null));
+ }
+
+ @Test
+ void toDouble_EmptyOptionalDouble_ReturnsNull() {
+ Double result = OptionalTools.toDouble(OptionalDouble.empty());
+ assertNull(result);
+ }
+
+ @Test
+ void toDouble_PresentOptionalDouble_ReturnsValue() {
+ Double result = OptionalTools.toDouble(OptionalDouble.of(10.0));
+ assertEquals(10.0, result, 0.0001);
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java
new file mode 100644
index 0000000..2f63bc6
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/RandomToolsTests.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2024 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.util;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+@SuppressWarnings("null")
+public class RandomToolsTests {
+
+ private static Random random;
+ private static SecureRandom secureRandom;
+ private static char[] sourceCharactersArray;
+ private static String sourceCharactersString;
+
+ @BeforeAll
+ public static void setUp() {
+ random = new Random();
+ secureRandom = new SecureRandom();
+ sourceCharactersArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
+ sourceCharactersString = "abcdefghijklmnopqrstuvwxyz";
+ }
+
+ @Test
+ public void randomStr_NullRandom_ThrowsException() {
+ assertThrows(IllegalArgumentException.class,
+ () -> RandomTools.randomStr(null, sourceCharactersArray, 5));
+ assertThrows(IllegalArgumentException.class,
+ () -> RandomTools.randomStr(null, sourceCharactersString, 5));
+ }
+
+ @Test
+ public void randomStr_NullSourceCharacters_ThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, (char[]) null, 5));
+ assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, (String) null, 5));
+ }
+
+ @Test
+ public void randomStr_NegativeLength_ThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, sourceCharactersArray, -1));
+ }
+
+ @Test
+ public void randomStr_ZeroLength_ReturnsEmptyString() {
+ assertAll(
+ () -> assertEquals("", RandomTools.randomStr(random, sourceCharactersArray, 0)),
+ () -> assertEquals("", RandomTools.randomStr(random, sourceCharactersString, 0)),
+ () -> assertEquals("", RandomTools.randomStr(secureRandom, sourceCharactersArray, 0)),
+ () -> assertEquals("", RandomTools.randomStr(secureRandom, sourceCharactersString, 0)));
+ }
+
+ @Test
+ public void randomStr_PositiveLength_ReturnsRandomString() {
+ assertAll(
+ () -> assertEquals(5, RandomTools.randomStr(random, sourceCharactersArray, 5).length()),
+ () -> assertEquals(5, RandomTools.randomStr(random, sourceCharactersString, 5).length()),
+ () -> assertEquals(5, RandomTools.randomStr(secureRandom, sourceCharactersArray, 5).length()),
+ () -> assertEquals(5, RandomTools.randomStr(secureRandom, sourceCharactersString, 5).length()));
+ }
+
+ @Test
+ public void randomStr_ReturnsRandomString() {
+ String result = RandomTools.randomStr(sourceCharactersArray, 5);
+ assertEquals(5, result.length());
+ }
+
+ @Test
+ public void randomStr_StringSourceCharacters_ReturnsRandomString() {
+ String result = RandomTools.randomStr(sourceCharactersString, 5);
+ assertEquals(5, result.length());
+ }
+
+ @Test
+ public void secureRandomStr_ReturnsRandomString() {
+ String result = RandomTools.secureRandomStr(sourceCharactersArray, 5);
+ assertEquals(5, result.length());
+ }
+
+ @Test
+ public void secureRandomStr_StringSourceCharacters_ReturnsRandomString() {
+ String result = RandomTools.secureRandomStr(sourceCharactersString, 5);
+ assertEquals(5, result.length());
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/RefTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/RefTests.java
deleted file mode 100644
index 34e9fa3..0000000
--- a/src/test/java/xyz/zhouxy/plusone/commons/util/RefTests.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2024 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.util;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.junit.jupiter.api.Test;
-
-import lombok.extern.slf4j.Slf4j;
-import xyz.zhouxy.plusone.commons.base.BoolRef;
-import xyz.zhouxy.plusone.commons.base.CharRef;
-import xyz.zhouxy.plusone.commons.base.DoubleRef;
-import xyz.zhouxy.plusone.commons.base.IntRef;
-import xyz.zhouxy.plusone.commons.base.LongRef;
-import xyz.zhouxy.plusone.commons.base.Ref;
-
-@Slf4j
-class RefTests {
-
- @Test
- void testRef() {
- Ref strRef = new Ref<>("ZhouXY");
- apply(strRef);
- assertEquals("Hello ZhouXY", strRef.getValue());
- log.info("strRef: {}", strRef);
- }
-
- void apply(Ref strRef) {
- strRef.transform(str -> "Hello " + str);
- }
-
- @Test
- void testBoolRef() {
- BoolRef boolRef = new BoolRef(false);
- apply(boolRef);
- assertTrue(boolRef.getValue());
- log.info("boolRef: {}", boolRef);
- }
-
- void apply(BoolRef boolRef) {
- boolRef.setValue(true);
- }
-
- @Test
- void testCharRef() {
- CharRef charRef = new CharRef('T');
-
- apply(false, charRef);
- assertEquals('0', charRef.getValue());
- log.info("charRef: {}", charRef);
-
- apply(true, charRef);
- assertEquals('1', charRef.getValue());
- log.info("charRef: {}", charRef);
- }
-
- void apply(boolean condition, CharRef charRef) {
- charRef.apply(c -> condition ? '1' : '0');
- }
-
- @Test
- void testDoubleRef() {
- DoubleRef doubleRef = new DoubleRef(2.33);
- apply(88.108, doubleRef);
- assertEquals(2.33 * 88.108, doubleRef.getValue());
- log.info("doubleRef: {}", doubleRef);
- }
-
- void apply(double num, DoubleRef doubleRef) {
- doubleRef.apply(d -> d * num);
- }
-
- @Test
- void testIntRef() {
- IntRef intRef = new IntRef(108);
- apply(88, intRef);
- assertEquals(108 - 88, intRef.getValue());
- log.info("intRef: {}", intRef);
- }
-
- void apply(int num, IntRef intRef) {
- intRef.apply(d -> d - num);
- }
-
- @Test
- void testLongRef() {
- LongRef longRef = new LongRef(108L);
- apply(88L, longRef);
- assertEquals(108L + 88L, longRef.getValue());
- log.info("intRef: {}", longRef);
- }
-
- void apply(long num, LongRef longRef) {
- longRef.apply(d -> d + num);
- }
-}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java
new file mode 100644
index 0000000..0d3fb8d
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/RegexToolsTests.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2024 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.util;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.jupiter.api.Test;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public
+class RegexToolsTests {
+
+ @Test
+ void getPattern_CachePatternTrue_ReturnsCachedPattern() {
+ String pattern = "abc";
+ Pattern cachedPattern = RegexTools.getPattern(pattern, true);
+ Pattern patternFromCache = RegexTools.getPattern(pattern, true);
+ assertSame(cachedPattern, patternFromCache, "Pattern should be cached");
+ }
+
+ @Test
+ void getPattern_CachePatternFalse_ReturnsNewPattern() {
+ String pattern = "getPattern_CachePatternFalse_ReturnsNewPattern";
+ Pattern pattern1 = RegexTools.getPattern(pattern, false);
+ Pattern pattern2 = RegexTools.getPattern(pattern, false);
+ assertNotSame(pattern1, pattern2, "Pattern should not be cached");
+ }
+
+ @Test
+ void getPattern_NullPattern_ThrowsException() {
+ assertThrows(NullPointerException.class, () -> {
+ RegexTools.getPattern(null, true);
+ });
+ }
+
+ @Test
+ void getPatterns_CachePatternTrue_ReturnsCachedPatterns() {
+ String[] patterns = {"abc", "def"};
+ Pattern[] cachedPatterns = RegexTools.getPatterns(patterns, true);
+ Pattern[] patternsFromCache = RegexTools.getPatterns(patterns, true);
+ assertSame(cachedPatterns[0], patternsFromCache[0]);
+ assertSame(cachedPatterns[1], patternsFromCache[1]);
+ }
+
+ @Test
+ void getPatterns_CachePatternFalse_ReturnsNewPatterns() {
+ String[] patterns = {"getPatterns_CachePatternFalse_ReturnsNewPatterns1", "getPatterns_CachePatternFalse_ReturnsNewPatterns2"};
+ Pattern[] patterns1 = RegexTools.getPatterns(patterns, false);
+ Pattern[] patterns2 = RegexTools.getPatterns(patterns, false);
+ assertNotSame(patterns1[0], patterns2[0]);
+ assertNotSame(patterns1[1], patterns2[1]);
+ }
+
+ @Test
+ void getPatterns_NullPatterns_ThrowsException() {
+ assertThrows(NullPointerException.class, () -> {
+ RegexTools.getPatterns(null, true);
+ });
+ }
+
+ @Test
+ void matches_InputMatchesPattern_ReturnsTrue() {
+ String pattern = "abc";
+ Pattern compiledPattern = Pattern.compile(pattern);
+ assertTrue(RegexTools.matches("abc", compiledPattern), "Input should match pattern");
+ }
+
+ @Test
+ void matches_InputDoesNotMatchPattern_ReturnsFalse() {
+ String pattern = "abc";
+ Pattern compiledPattern = Pattern.compile(pattern);
+ assertFalse(RegexTools.matches("abcd", compiledPattern), "Input should not match pattern");
+ }
+
+ @Test
+ void matches_NullInput_ReturnsFalse() {
+ String pattern = "abc";
+ Pattern compiledPattern = Pattern.compile(pattern);
+ assertFalse(RegexTools.matches(null, compiledPattern), "Null input should return false");
+ }
+
+ @Test
+ void matchesOne_InputMatchesOnePattern_ReturnsTrue() {
+ String[] patterns = {"abc", "def"};
+ Pattern[] compiledPatterns = new Pattern[patterns.length];
+ for (int i = 0; i < patterns.length; i++) {
+ compiledPatterns[i] = Pattern.compile(patterns[i]);
+ }
+ assertTrue(RegexTools.matchesOne("abc", compiledPatterns), "Input should match one pattern");
+ }
+
+ @Test
+ void matchesOne_InputDoesNotMatchAnyPattern_ReturnsFalse() {
+ String[] patterns = {"abc", "def"};
+ Pattern[] compiledPatterns = new Pattern[patterns.length];
+ for (int i = 0; i < patterns.length; i++) {
+ compiledPatterns[i] = Pattern.compile(patterns[i]);
+ }
+ assertFalse(RegexTools.matchesOne("xyz", compiledPatterns), "Input should not match any pattern");
+ }
+
+ @Test
+ void matchesAll_InputMatchesAllPatterns_ReturnsTrue() {
+ String[] patterns = {"abc", "abc"};
+ Pattern[] compiledPatterns = new Pattern[patterns.length];
+ for (int i = 0; i < patterns.length; i++) {
+ compiledPatterns[i] = Pattern.compile(patterns[i]);
+ }
+ assertTrue(RegexTools.matchesAll("abc", compiledPatterns), "Input should match all patterns");
+ }
+
+ @Test
+ void matchesAll_InputDoesNotMatchAllPatterns_ReturnsFalse() {
+ String[] patterns = {"abc", "def"};
+ Pattern[] compiledPatterns = new Pattern[patterns.length];
+ for (int i = 0; i < patterns.length; i++) {
+ compiledPatterns[i] = Pattern.compile(patterns[i]);
+ }
+ assertFalse(RegexTools.matchesAll("abc", compiledPatterns), "Input should not match all patterns");
+ }
+
+ @Test
+ void getMatcher_ValidInputAndPattern_ReturnsMatcher() {
+ String pattern = "abc";
+ Pattern compiledPattern = Pattern.compile(pattern);
+ Matcher matcher = RegexTools.getMatcher("abc", compiledPattern);
+ assertNotNull(matcher, "Matcher should not be null");
+ }
+
+ @Test
+ void getMatcher_NullInput_ThrowsException() {
+ String pattern = "abc";
+ Pattern compiledPattern = Pattern.compile(pattern);
+ assertThrows(NullPointerException.class, () -> {
+ RegexTools.getMatcher(null, compiledPattern);
+ });
+ }
+
+ @Test
+ void getMatcher_NullPattern_ThrowsException() {
+ final Pattern pattern = null;
+ assertThrows(NullPointerException.class, () -> {
+ RegexTools.getMatcher("abc", pattern);
+ });
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/StringToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/StringToolsTests.java
new file mode 100644
index 0000000..b383ee6
--- /dev/null
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/StringToolsTests.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 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.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+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 org.junit.jupiter.api.Test;
+
+public
+class StringToolsTests {
+
+ @Test
+ void isNotBlank_NullString_ReturnsFalse() {
+ assertFalse(StringTools.isNotBlank(null));
+ }
+
+ @Test
+ void isNotBlank_EmptyString_ReturnsFalse() {
+ assertFalse(StringTools.isNotBlank(""));
+ }
+
+ @Test
+ void isNotBlank_WhitespaceString_ReturnsFalse() {
+ assertFalse(StringTools.isNotBlank(" "));
+ }
+
+ @Test
+ void isNotBlank_NonWhitespaceString_ReturnsTrue() {
+ assertTrue(StringTools.isNotBlank("Hello"));
+ }
+
+ @Test
+ void repeat_NullString_ThrowsException() {
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
+ StringTools.repeat(null, 2);
+ });
+ assertNull(exception.getMessage());
+ }
+
+ @Test
+ void repeat_EmptyString_ReturnsEmptyString() {
+ assertEquals("", StringTools.repeat("", 2));
+ }
+
+ @Test
+ void repeat_RepeatOnce_ReturnsOriginalString() {
+ assertEquals("Hello", StringTools.repeat("Hello", 1));
+ }
+
+ @Test
+ void repeat_RepeatMultipleTimes_ReturnsRepeatedString() {
+ assertEquals("HelloHelloHello", StringTools.repeat("Hello", 3));
+ }
+
+ @Test
+ void repeat_ExceedsMaxLength_ReturnsTruncatedString() {
+ assertEquals("HelloHello", StringTools.repeat("Hello", 2, 14));
+ assertEquals("HelloHel", StringTools.repeat("Hello", 2, 8));
+ assertEquals("He", StringTools.repeat("Hello", 2, 2));
+ assertEquals("", StringTools.repeat("Hello", 0, 2));
+ assertThrows(IllegalArgumentException.class, () -> StringTools.repeat("Hello", -1, 2));
+ assertThrows(IllegalArgumentException.class, () -> StringTools.repeat("Hello", 5, -1));
+ }
+
+ @Test
+ void repeat_ZeroTimes_ReturnsEmptyString() {
+ assertEquals("", StringTools.repeat("Hello", 0));
+ }
+}
diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java
index 753e267..4249c40 100644
--- a/src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java
+++ b/src/test/java/xyz/zhouxy/plusone/commons/util/TreeBuilderTests.java
@@ -16,8 +16,14 @@
package xyz.zhouxy.plusone.commons.util;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -25,143 +31,203 @@ import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import cn.hutool.core.util.ObjectUtil;
+import lombok.EqualsAndHashCode;
import lombok.ToString;
class TreeBuilderTests {
private static final Logger log = LoggerFactory.getLogger(TreeBuilderTests.class);
+
+ private final MenuItem A = MenuItem.of("A", "首页", "/home", 1);
+ private final MenuList B = MenuList.of("B", "系统管理", 3);
+ private final MenuItem B001 = /**/MenuItem.of("B", "B001", "功能管理", "/sys/function-mgmt", 4);
+ private final MenuItem B002 = /**/MenuItem.of("B", "B002", "角色管理", "/sys/role-mgmt", 3);
+ private final MenuItem B003 = /**/MenuItem.of("B", "B003", "账号管理", "/sys/account-mgmt", 2);
+ private final MenuItem B004 = /**/MenuItem.of("B", "B004", "系统参数管理", "/sys/param-mgmt", 1);
+ private final MenuList C = MenuList.of("C", "一级菜单C", 2);
+ private final MenuList C1 = /**/MenuList.of("C", "C1", "二级菜单C1", 3);
+ private final MenuItem C1001 = /**//**/MenuItem.of("C1", "C1001", "三级菜单C1001", "/c/c1/c1001", 1);
+ private final MenuItem C1002 = /**//**/MenuItem.of("C1", "C1002", "三级菜单C1002", "/c/c1/c1002", 2);
+ private final MenuItem C2 = /**/MenuItem.of("C", "C2", "二级菜单C2", "/c/c2", 1);
+ private final MenuItem C3 = /**/MenuItem.of("C", "C3", "二级菜单C3", "/c/c3", 2);
+
+ private final List menus = ImmutableList.of(B, C1002, A, B004, C3, B001, B003, C1, C1001, B002, C, C2);
+
private final TreeBuilder treeBuilder = new TreeBuilder<>(
Menu::getMenuCode,
menu -> Optional.ofNullable(menu.parentMenuCode),
MenuList::addChild,
- (a, b) -> Integer.compare(a.getOrderNum(), b.getOrderNum()));
+ Menu.orderNumComparator);
@Test
- void testBuildTree() {
- List menus = Lists.newArrayList(
- MenuList.of("B", "系统管理", 3),
- MenuItem.of("A", "首页", "/home", 1),
- /**/MenuItem.of("B", "B002", "角色管理", "/sys/role-mgmt", 3),
- /**/MenuItem.of("B", "B001", "功能管理", "/sys/function-mgmt", 4),
- /**/MenuItem.of("B", "B004", "系统参数管理", "/sys/param-mgmt", 1),
- /**/MenuItem.of("B", "B003", "账号管理", "/sys/account-mgmt", 2),
- MenuList.of("C", "一级菜单C", 2),
- /**/MenuItem.of("C", "C3", "二级菜单C3", "/c/c3", 2),
- /**/MenuList.of("C", "C1", "二级菜单C1", 2),
- /**//**/MenuItem.of("C1", "C1001", "三级菜单C1001", "/c/c1/c1001", 1),
- /**//**/MenuItem.of("C1", "C1002", "三级菜单C1002", "/c/c1/c1002", 2),
- /**/MenuItem.of("C", "C2", "二级菜单C2", "/c/c2", 1));
-
- List clonedMenus;
-
- clonedMenus = menus.stream().map(m -> ObjectUtil.clone(m)).collect(Collectors.toList());
+ void testBuildTreeAndSortedByOrderNum() {
+ List clonedMenus = menus.stream().map(ObjectUtil::clone).collect(Collectors.toList());
List menuTreeSortedByOrderNum = treeBuilder.buildTree(clonedMenus);
log.info("menuTreeSortedByOrderNum: {}", new Gson().toJson(menuTreeSortedByOrderNum));
- clonedMenus = menus.stream().map(m -> ObjectUtil.clone(m)).collect(Collectors.toList());
+ assertEquals(clonedMenus.stream()
+ .filter(menu -> menu.getParentMenuCode() == null)
+ .sorted(Menu.orderNumComparator)
+ .collect(Collectors.toList()),
+ menuTreeSortedByOrderNum);
+
+ Map menuMap = new HashMap<>();
+ for (Menu element : clonedMenus) {
+ menuMap.put(element.getMenuCode(), element);
+ }
+
+ assertEquals(Arrays.stream(new Menu[] { B001, B002, B003, B004 })
+ .sorted(Menu.orderNumComparator)
+ .collect(Collectors.toList()),
+ MenuList.class.cast(menuMap.get("B")).children);
+
+ assertEquals(Arrays.stream(new Menu[] { C1, C2, C3 })
+ .sorted(Menu.orderNumComparator)
+ .collect(Collectors.toList()),
+ MenuList.class.cast(menuMap.get("C")).children);
+
+ assertEquals(Arrays.stream(new Menu[] { C1001, C1002 })
+ .sorted(Menu.orderNumComparator)
+ .collect(Collectors.toList()),
+ MenuList.class.cast(menuMap.get("C1")).children);
+
+ }
+
+ @Test
+ void testBuildTreeAndSortedByMenuCode() {
+ List clonedMenus;
+
+ clonedMenus = menus.stream().map(ObjectUtil::clone).collect(Collectors.toList());
List menuTreeSortedByMenuCode = treeBuilder.buildTree(
clonedMenus,
- (a, b) -> a.getMenuCode().compareTo(b.getMenuCode())
- );
+ (a, b) -> a.getMenuCode().compareTo(b.getMenuCode()));
log.info("menuTreeSortedByMenuCode: {}", new Gson().toJson(menuTreeSortedByMenuCode));
- }
-}
-@ToString
-abstract class Menu implements Serializable {
- protected final String parentMenuCode;
- protected final String menuCode;
- protected final String title;
- protected final int orderNum;
+ assertEquals(clonedMenus.stream()
+ .filter(menu -> menu.getParentMenuCode() == null)
+ .sorted((a, b) -> a.getMenuCode().compareTo(b.getMenuCode()))
+ .collect(Collectors.toList()),
+ menuTreeSortedByMenuCode);
- public Menu(String parentMenuCode, String menuCode, String title, int orderNum) {
- this.parentMenuCode = parentMenuCode;
- this.menuCode = menuCode;
- this.title = title;
- this.orderNum = orderNum;
- }
-
- public String getMenuCode() {
- return menuCode;
- }
-
- public String getParentMenuCode() {
- return parentMenuCode;
- }
-
- public String getTitle() {
- return title;
- }
-
- public int getOrderNum() {
- return orderNum;
- }
-
- private static final long serialVersionUID = 20240917181424L;
-}
-
-@ToString(callSuper = true)
-class MenuItem extends Menu {
-
- private final String url;
-
- private MenuItem(String parentMenuCode, String menuCode, String title, String url, int orderNum) {
- super(parentMenuCode, menuCode, title, orderNum);
- this.url = url;
- }
-
- static MenuItem of(String parentMenuCode, String menuCode, String title, String url, int orderNum) {
- return new MenuItem(parentMenuCode, menuCode, title, url, orderNum);
- }
-
- static MenuItem of(String menuCode, String title, String url, int orderNum) {
- return new MenuItem(null, menuCode, title, url, orderNum);
- }
-
- public String getUrl() {
- return url;
- }
-
- private static final long serialVersionUID = 20240917181910L;
-}
-
-@ToString(callSuper = true)
-class MenuList extends Menu {
-
- private List children;
-
- private MenuList(String parentMenuCode, String menuCode, String title, int orderNum) {
- super(parentMenuCode, menuCode, title, orderNum);
- }
-
- static MenuList of(String parentMenuCode, String menuCode, String title, int orderNum) {
- return new MenuList(parentMenuCode, menuCode, title, orderNum);
- }
-
- static MenuList of(String menuCode, String title, int orderNum) {
- return new MenuList(null, menuCode, title, orderNum);
- }
-
- static MenuList of(String menuCode, String title, Iterable children, int orderNum) {
- return of(null, menuCode, title, children, orderNum);
- }
-
- static MenuList of(String parentMenuCode, String menuCode, String title, Iterable children, int orderNum) {
- final MenuList instance = of(parentMenuCode, menuCode, title, orderNum);
- children.forEach(instance::addChild);
- return instance;
- }
-
- public void addChild(Menu child) {
- if (this.children == null) {
- this.children = Lists.newArrayList();
+ Map menuMap = new HashMap<>();
+ for (Menu element : clonedMenus) {
+ menuMap.put(element.getMenuCode(), element);
}
- this.children.add(child);
+
+ assertEquals(ImmutableList.of(B001, B002, B003, B004),
+ MenuList.class.cast(menuMap.get("B")).children);
+
+ assertEquals(ImmutableList.of(C1, C2, C3),
+ MenuList.class.cast(menuMap.get("C")).children);
+
+ assertEquals(ImmutableList.of(C1001, C1002),
+ MenuList.class.cast(menuMap.get("C1")).children);
}
- private static final long serialVersionUID = 20240917181917L;
+ @ToString
+ @EqualsAndHashCode
+ private static abstract class Menu implements Serializable { // NOSONAR
+ protected final String parentMenuCode;
+ protected final String menuCode;
+ protected final String title;
+ protected final int orderNum;
+
+ public Menu(String parentMenuCode, String menuCode, String title, int orderNum) {
+ this.parentMenuCode = parentMenuCode;
+ this.menuCode = menuCode;
+ this.title = title;
+ this.orderNum = orderNum;
+ }
+
+ public String getMenuCode() {
+ return menuCode;
+ }
+
+ public String getParentMenuCode() {
+ return parentMenuCode;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public int getOrderNum() {
+ return orderNum;
+ }
+
+ public static Comparator orderNumComparator =
+ (a, b) -> Integer.compare(a.getOrderNum(), b.getOrderNum());
+
+ private static final long serialVersionUID = 20240917181424L;
+ }
+
+ @ToString(callSuper = true)
+ @EqualsAndHashCode(callSuper = true)
+ private static final class MenuItem extends Menu {
+
+ private final String url;
+
+ private MenuItem(String parentMenuCode, String menuCode, String title, String url, int orderNum) {
+ super(parentMenuCode, menuCode, title, orderNum);
+ this.url = url;
+ }
+
+ static MenuItem of(String parentMenuCode, String menuCode, String title, String url, int orderNum) {
+ return new MenuItem(parentMenuCode, menuCode, title, url, orderNum);
+ }
+
+ static MenuItem of(String menuCode, String title, String url, int orderNum) {
+ return new MenuItem(null, menuCode, title, url, orderNum);
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ private static final long serialVersionUID = 20240917181910L;
+ }
+
+ @ToString(callSuper = true)
+ private static final class MenuList extends Menu {
+
+ private List children;
+
+ private MenuList(String parentMenuCode, String menuCode, String title, int orderNum) {
+ super(parentMenuCode, menuCode, title, orderNum);
+ }
+
+ static MenuList of(String parentMenuCode, String menuCode, String title, int orderNum) {
+ return new MenuList(parentMenuCode, menuCode, title, orderNum);
+ }
+
+ static MenuList of(String menuCode, String title, int orderNum) {
+ return new MenuList(null, menuCode, title, orderNum);
+ }
+
+ @SuppressWarnings("unused")
+ static MenuList of(String menuCode, String title, Iterable children, int orderNum) {
+ return of(null, menuCode, title, children, orderNum);
+ }
+
+ static MenuList of(String parentMenuCode, String menuCode, String title, Iterable children,
+ int orderNum) {
+ final MenuList instance = of(parentMenuCode, menuCode, title, orderNum);
+ children.forEach(instance::addChild);
+ return instance;
+ }
+
+ public void addChild(Menu child) {
+ if (this.children == null) {
+ this.children = Lists.newArrayList();
+ }
+ this.children.add(child);
+ }
+
+ private static final long serialVersionUID = 20240917181917L;
+ }
}
diff --git a/src/test/resources/mybatis-config.xml b/src/test/resources/mybatis-config.xml
new file mode 100644
index 0000000..903a7bb
--- /dev/null
+++ b/src/test/resources/mybatis-config.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/xyz/zhouxy/plusone/commons/model/dto/test/AccountQueries/AccountQueries.xml b/src/test/resources/xyz/zhouxy/plusone/commons/model/dto/test/AccountQueries/AccountQueries.xml
new file mode 100644
index 0000000..af25f08
--- /dev/null
+++ b/src/test/resources/xyz/zhouxy/plusone/commons/model/dto/test/AccountQueries/AccountQueries.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SELECT id, username, email, status, created_by, create_time, version
+ FROM sys_account a
+
+
+ AND a.id = #{query.id}
+
+
+ AND a.username = #{query.username}
+
+
+ AND a.email = #{query.email}
+
+
+ AND a.status = #{query.status}
+
+
+ AND a.created_by = #{query.createdBy}
+
+
+ AND a.create_time >= #{query.createTimeStart}
+
+
+ AND a.create_time < #{query.createTimeEnd}
+
+
+ ORDER BY
+
+ ${property.sqlSnippet},
+
+ id ASC
+ LIMIT #{page.size} OFFSET #{page.offset}
+
+
+
+ SELECT count(*)
+ FROM sys_account a
+
+
+ AND a.id = #{query.id}
+
+
+ AND a.username = #{query.username}
+
+
+ AND a.email = #{query.email}
+
+
+ AND a.status = #{query.status}
+
+
+ AND a.created_by = #{query.createdBy}
+
+
+ AND a.create_time >= #{query.createTimeStart}
+
+
+ AND a.create_time < #{query.createTimeEnd}
+
+
+
+
+