1 Commits

Author SHA1 Message Date
ddc08da33e 新增 MoreInetAddresses 工具类 2023-09-22 00:37:46 +08:00
113 changed files with 2591 additions and 6673 deletions

View File

@@ -1,14 +0,0 @@
root = true
[*]
# 缩进
indent_style = space
indent_size = 4
# 行尾
end_of_line = lf
# 字符集
charset = utf-8
# 删除行尾空格
trim_trailing_whitespace = true
# 文件最后插入空行
insert_final_newline = true

View File

@@ -1,100 +0,0 @@
[x] 无需测试
[ ] 未开始测试
[-] 测试未完成
[Y] 测试完成
xyz.zhouxy.plusone.commons
├───annotation
│ ReaderMethod.java [x]
│ StaticFactoryMethod.java [x]
│ UnsupportedOperation.java [x]
│ ValueObject.java [x]
│ Virtual.java [x]
│ WriterMethod.java [x]
├───base
│ BoolRef.java [Y]
│ CharRef.java [Y]
│ DoubleRef.java [Y]
│ IntRef.java [Y]
│ IWithCode.java [ ]
│ IWithIntCode.java [ ]
│ IWithLongCode.java [ ]
│ JRE.java [ ]
│ LongRef.java [Y]
│ Ref.java [Y]
├───collection
│ AbstractMapWrapper.java [ ]
│ CollectionTools.java [ ]
│ MapWrapper.java [ ]
│ ReadWriteLockedTable.java [ ]
│ SafeConcurrentHashMap.java [ ]
├───constant
│ PatternConsts.java [ ]
│ RegexConsts.java [ ]
├───exception
│ │ ParsingFailureException.java [ ]
│ │
│ ├───business
│ │ BizException.java [ ]
│ │ InvalidInputException.java [ ]
│ │ RequestParamsException.java [ ]
│ │
│ └───system
│ DataOperationResultException.java [ ]
│ NoAvailableMacFoundException.java [ ]
│ SysException.java [ ]
├───function
│ BoolUnaryOperator.java [ ]
│ CharUnaryOperator.java [ ]
│ Executable.java [ ]
│ OptionalSupplier.java [ ]
│ PredicateTools.java [ ]
│ ThrowingConsumer.java [ ]
│ ThrowingPredicate.java [ ]
│ ThrowingSupplier.java [ ]
│ ToOptionalBiFunction.java [ ]
│ ToOptionalFunction.java [ ]
├───model
│ │ Chinese2ndGenIDCardNumber.java [-]
│ │ Gender.java [ ]
│ │ IDCardNumber.java [ ]
│ │ ValidatableStringRecord.java [-]
│ │
│ └───dto
│ PageResult.java [-]
│ PagingAndSortingQueryParams.java [-]
│ PagingParams.java [-]
│ UnifiedResponse.java [-]
├───sql
│ JdbcSql.java [ ]
│ MyBatisSql.java [-]
│ SQL.java [ ]
├───time
│ Quarter.java [-]
│ YearQuarter.java [-]
└───util
ArrayTools.java [-]
AssertTools.java [-]
BigDecimals.java [-]
ConcurrentHashMapTools.java [-]
DateTimeTools.java [-]
Enumeration.java [Y]
EnumTools.java [-]
IdGenerator.java [ ]
IdWorker.java [ ]
Numbers.java [ ]
OptionalTools.java [ ]
RandomTools.java [ ]
RegexTools.java [ ]
SnowflakeIdGenerator.java [ ]
StringTools.java [ ]
TreeBuilder.java [-]

98
pom.xml
View File

@@ -6,25 +6,44 @@
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-commons</artifactId>
<version>1.0.0-alpha</version>
<version>0.1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<commons-lang3.version>3.17.0</commons-lang3.version>
<guava.version>33.3.1-jre</guava.version>
<joda-time.version>2.13.0</joda-time.version>
<jackson.version>2.13.5</jackson.version>
<commons-lang3.version>3.13.0</commons-lang3.version>
<guava.version>32.0.1-jre</guava.version>
<google-jsr305.version>3.0.2</google-jsr305.version>
<joda-time.version>2.12.5</joda-time.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>${google-jsr305.version}</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
@@ -32,15 +51,6 @@
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
@@ -54,18 +64,6 @@
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
@@ -78,28 +76,7 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
<scope>test</scope>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.5</version>
<scope>test</scope>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
<version>5.8.21</version>
<scope>test</scope>
</dependency>
@@ -147,29 +124,4 @@
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>aliyun</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -22,13 +22,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标识该方法是可虚方法
* 标识该方法是可覆写的
* <p>该注解用于提醒强调父类虽然有默认实现但子类可以根据自己的需要覆写</p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Virtual {
public @interface Overridable {
}

View File

@@ -1,19 +1,3 @@
/*
* 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.annotation;
import java.lang.annotation.ElementType;
@@ -21,15 +5,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* ReaderMethod
*
* <p>
* 标识方法是读方法,如 getter。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0
*/
// TODO 添加 Javadoc
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReaderMethod {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -26,7 +26,7 @@ import java.lang.annotation.Target;
*
* <p>标识方法为静态工厂方法
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
*/
@Target(ElementType.METHOD)

View File

@@ -1,19 +1,3 @@
/*
* 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.annotation;
import java.lang.annotation.ElementType;
@@ -22,16 +6,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Documented;
/**
* UnsupportedOperation
*
* <p>标识方法为不支持的操作。该方法将抛出 {@link UnsupportedOperationException}。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @version 1.0
* @since 1.0
* @see UnsupportedOperationException
*/
@Documented
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -17,7 +17,6 @@
package xyz.zhouxy.plusone.commons.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -25,10 +24,9 @@ import java.lang.annotation.Target;
/**
* ValueObject - 值对象
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
*/
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValueObject {

View File

@@ -1,19 +1,3 @@
/*
* 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.annotation;
import java.lang.annotation.ElementType;
@@ -21,15 +5,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* WriterMethod
*
* <p>
* 标识方法是写方法,如 setter。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0
*/
// TODO 添加 Javadoc
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WriterMethod {

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-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.
@@ -16,8 +16,6 @@
package xyz.zhouxy.plusone.commons.base;
import java.util.Objects;
import javax.annotation.Nonnull;
/**
@@ -25,13 +23,9 @@ import javax.annotation.Nonnull;
* 用于像自定义异常等需要带有 {@code code} 字段的类,
* 方便其它地方的程序判断该类的是否实现了此接口,以此获取其实例的 {@code code} 字段的值。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public interface IWithCode<T> {
@Nonnull
T getCode();
default boolean equalsCode(T code) {
return Objects.equals(getCode(), code);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-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.
@@ -21,12 +21,8 @@ package xyz.zhouxy.plusone.commons.base;
* 用于像自定义异常等需要带有 {@code code} 字段的类,
* 方便其它地方的程序判断该类的是否实现了此接口,以此获取其实例的 {@code code} 字段的值。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public interface IWithIntCode {
int getCode();
default boolean equalsCode(int code) {
return getCode() == code;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-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.
@@ -21,12 +21,8 @@ package xyz.zhouxy.plusone.commons.base;
* 用于像自定义异常等需要带有 {@code code} 字段的类,
* 方便其它地方的程序判断该类的是否实现了此接口,以此获取其实例的 {@code code} 字段的值。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public interface IWithLongCode {
long getCode();
default boolean equalsCode(long code) {
return getCode() == code;
}
}

View File

@@ -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;
}
}

View File

@@ -1,11 +1,12 @@
/*
* Copyright 2023-2024 the original author or authors.
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* 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
* http://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,
@@ -16,7 +17,8 @@
package xyz.zhouxy.plusone.commons.base;
import xyz.zhouxy.plusone.commons.util.StringTools;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
/**
* JRE version
@@ -33,30 +35,23 @@ public class JRE {
private static int getJre() {
String version = System.getProperty("java.version");
boolean isNotBlank = StringTools.isNotBlank(version);
if (isNotBlank && version.startsWith("1.8")) {
boolean isBlank = StringUtils.isBlank(version);
if (!isBlank && 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);
Object javaRunTimeVersion = MethodUtils.invokeMethod(Runtime.getRuntime(), "version");
return (int) MethodUtils.invokeMethod(javaRunTimeVersion, "major");
} catch (Exception e) {
// Can't determine current JRE version (maybe java.version is null),
// 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");
}

View File

@@ -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;
}
}

View File

@@ -1,87 +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.Objects;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import com.google.common.annotations.Beta;
@Beta
public final class Ref<T> {
private T value;
public Ref() {
this.value = null;
}
public Ref(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public void transform(UnaryOperator<T> operator) {
this.value = operator.apply(this.value);
}
public boolean isNull() {
return this.value == null;
}
public boolean isNotNull() {
return this.value != null;
}
public void execute(Consumer<? super T> consumer) {
consumer.accept(value);
}
@Override
public String toString() {
return String.format("Ref[%s]", value);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Ref<?> other = (Ref<?>) obj;
return Objects.equals(this.value, other.value);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -20,7 +20,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -31,7 +30,7 @@ import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools;
import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapUtil;
@Beta
public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V, T>> {
@@ -58,7 +57,9 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
}
public final T putAll(Map<? extends K, ? extends V> m) {
m.forEach(this::put);
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
return getSelf();
}
@@ -78,6 +79,21 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
return Optional.ofNullable(this.map.get(key));
}
/**
* 获取 {@code map} 中的值。如果 {@code key} 不存在,则抛出异常。
*
* @param key 键
* @return 值
* @throws IllegalArgumentException key 不存在时抛出。
*/
@Nullable
public V getOrNull(K key) {
if (!this.map.containsKey(key)) {
throw new IllegalArgumentException("Key does not exist");
}
return this.map.get(key);
}
@SuppressWarnings("unchecked")
public final <R> Optional<R> getAndConvert(K key) {
return get(key).map(v -> (R) v);
@@ -124,31 +140,15 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
}
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<K, V>) this.map, key, func);
return ConcurrentHashMapUtil.computeIfAbsent(
(ConcurrentHashMap<K, V>) this.map, key, mappingFunction);
} else {
return this.map.computeIfAbsent(key, func);
return this.map.computeIfAbsent(key, mappingFunction);
}
}
@@ -156,7 +156,7 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
return this.map;
}
public final Map<K, V> exportUnmodifiableMap() {
public final Map<K, V> exportUnmodifiableMapMap() {
return Collections.unmodifiableMap(this.map);
}
@@ -167,24 +167,6 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
return this.map.toString();
}
@Override
public int hashCode() {
return Objects.hash(map);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("rawtypes")
AbstractMapWrapper other = (AbstractMapWrapper) obj;
return Objects.equals(map, other.map);
}
public abstract static class Builder<K, V, T extends AbstractMapWrapper<K, V, T>> {
protected final Map<K, V> map;
protected Consumer<K> keyChecker;
@@ -216,7 +198,9 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
}
public Builder<K, V, T> putAll(Map<? extends K, ? extends V> m) {
m.forEach(this::put);
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
return this;
}

View File

@@ -1,49 +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.Map;
import javax.annotation.Nullable;
public class CollectionTools {
// isEmpty
public static boolean isEmpty(@Nullable Collection<?> collection) {
return collection == null || collection.isEmpty();
}
public static boolean isEmpty(@Nullable Map<?, ?> map) {
return map == null || map.isEmpty();
}
// isNotEmpty
public static boolean isNotEmpty(@Nullable Collection<?> collection) {
return collection != null && !collection.isEmpty();
}
public static boolean isNotEmpty(@Nullable Map<?, ?> map) {
return map != null && !map.isEmpty();
}
private CollectionTools() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,19 +1,3 @@
/*
* 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;
@@ -22,8 +6,6 @@ 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;
@@ -46,47 +28,46 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* <b>NOTE: 如果 {@link Table} 不需要更改请使用 {@link ImmutableTable}</b>
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108/">ZhouXY</a>
* @see Table
* @see ImmutableTable
* @see ReentrantReadWriteLock
* @since 0.1.0-SNAPSHOT
*/
@Beta
@ThreadSafe
public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
public class LockedTable<R, C, V> implements Table<R, C, V> {
private final Table<R, C, V> table;
private final ReentrantReadWriteLock.ReadLock readLock;
private final ReentrantReadWriteLock.WriteLock writeLock;
private ReadWriteLockedTable(Table<R, C, V> table, boolean fair) {
private LockedTable(Table<R, C, V> table, boolean fair) {
this.table = Objects.requireNonNull(table);
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(fair);
this.readLock = rwl.readLock();
this.writeLock = rwl.writeLock();
}
public static <R, C, V> ReadWriteLockedTable<R, C, V> of(Table<R, C, V> table) {
if (table instanceof ReadWriteLockedTable) {
return (ReadWriteLockedTable<R, C, V>) table;
public static <R, C, V> LockedTable<R, C, V> of(Table<R, C, V> table) {
if (table instanceof LockedTable) {
return (LockedTable<R, C, V>) table;
} else {
return new ReadWriteLockedTable<>(table, false);
return new LockedTable<>(table, false);
}
}
public static <R, C, V> ReadWriteLockedTable<R, C, V> of(Table<R, C, V> table, boolean fair) {
if (table instanceof ReadWriteLockedTable) {
return (ReadWriteLockedTable<R, C, V>) table;
public static <R, C, V> LockedTable<R, C, V> of(Table<R, C, V> table, boolean fair) {
if (table instanceof LockedTable) {
return (LockedTable<R, C, V>) table;
} else {
return new ReadWriteLockedTable<>(table, fair);
return new LockedTable<>(table, fair);
}
}
@Override
@ReaderMethod
public boolean contains(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
public boolean contains(Object rowKey, Object columnKey) {
readLock.lock();
try {
return this.table.contains(rowKey, columnKey);
@@ -97,7 +78,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@ReaderMethod
public boolean containsRow(@CheckForNull @Nonnull Object rowKey) {
public boolean containsRow(Object rowKey) {
readLock.lock();
try {
return this.table.containsRow(rowKey);
@@ -108,7 +89,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@ReaderMethod
public boolean containsColumn(@CheckForNull @Nonnull Object columnKey) {
public boolean containsColumn(Object columnKey) {
readLock.lock();
try {
return this.table.containsColumn(columnKey);
@@ -119,7 +100,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@ReaderMethod
public boolean containsValue(@CheckForNull @Nonnull Object value) {
public boolean containsValue(Object value) {
readLock.lock();
try {
return this.table.containsValue(value);
@@ -130,7 +111,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@ReaderMethod
public V get(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
public V get(Object rowKey, Object columnKey) {
readLock.lock();
try {
return this.table.get(rowKey, columnKey);
@@ -174,9 +155,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@WriterMethod
public V put(@CheckForNull @Nonnull R rowKey,
@CheckForNull @Nonnull C columnKey,
@CheckForNull @Nonnull V value) {
public V put(R rowKey, C columnKey, V value) {
writeLock.lock();
try {
return this.table.put(rowKey, columnKey, value);
@@ -187,7 +166,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@WriterMethod
public void putAll(@Nonnull Table<? extends R, ? extends C, ? extends V> table) {
public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
writeLock.lock();
try {
this.table.putAll(table);
@@ -198,7 +177,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@WriterMethod
public V remove(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
public V remove(Object rowKey, Object columnKey) {
writeLock.lock();
try {
return this.table.remove(rowKey, columnKey);
@@ -209,7 +188,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@ReaderMethod
public Map<C, V> row(@CheckForNull @Nonnull R rowKey) {
public Map<C, V> row(R rowKey) {
readLock.lock();
try {
return this.table.row(rowKey);
@@ -220,7 +199,7 @@ public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
@Override
@ReaderMethod
public Map<R, V> column(@CheckForNull @Nonnull C columnKey) {
public Map<R, V> column(C columnKey) {
readLock.lock();
try {
return this.table.column(columnKey);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -22,21 +22,9 @@ import java.util.function.Function;
import javax.annotation.concurrent.ThreadSafe;
import xyz.zhouxy.plusone.commons.base.JRE;
import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools;
import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapUtil;
/**
* SafeConcurrentHashMap
*
* <p>
* Java 8 的 {@link ConcurrentHashMap#computeIfAbsent(Object, Function)} 方法有 bug
* 使用 Java 8 时,可使用这个类进行替换。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0
* @see ConcurrentHashMap
* @see ConcurrentHashMapTools#computeIfAbsentForJava8(ConcurrentHashMap, Object, Function)
*/
// TODO 添加文档注释
@ThreadSafe
public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
@@ -114,8 +102,6 @@ public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
/** {@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);
return ConcurrentHashMapUtil.computeIfAbsentForJava8(this, key, mappingFunction);
}
}

View File

@@ -0,0 +1,205 @@
package xyz.zhouxy.plusone.commons.collection;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.annotation.concurrent.ThreadSafe;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
/**
* 将 {@link Table} 包装为同步集合,线程安全。
*
* <p>
* 可通过以下方式构建一个线程安全的 {@link Table}
* </p>
*
* <pre>
* SynchronizedTable.of(HashBasedTable.create())
* </pre>
*
* <p>
* <b>NOTE: 如果 {@link Table} 不需要更改,请使用 {@link ImmutableTable}</b>
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108/">ZhouXY</a>
* @since 0.1.0-SNAPSHOT
* @see Table
* @see ImmutableTable
*/
@Beta
@ThreadSafe
public class SynchronizedTable<R, C, V> implements Table<R, C, V>, Serializable {
private static final long serialVersionUID = 3716653837549439569L;
@SuppressWarnings("serial")
private final Table<R, C, V> table;
@SuppressWarnings("serial")
private final Object mutex;
private SynchronizedTable(Table<R, C, V> table) {
this.table = Objects.requireNonNull(table);
this.mutex = this;
}
private SynchronizedTable(Table<R, C, V> table, Object mutex) {
this.table = Objects.requireNonNull(table);
this.mutex = mutex;
}
public static <R, C, V> SynchronizedTable<R, C, V> of(Table<R, C, V> table) {
if (table instanceof SynchronizedTable) {
return (SynchronizedTable<R, C, V>) table;
} else {
return new SynchronizedTable<>(table);
}
}
public static <R, C, V> SynchronizedTable<R, C, V> of(Table<R, C, V> table, Object mutex) {
if (table instanceof SynchronizedTable) {
SynchronizedTable<R, C, V> srcTable = (SynchronizedTable<R, C, V>) table;
return new SynchronizedTable<>(srcTable.table, mutex);
} else {
return new SynchronizedTable<>(table);
}
}
@Override
public boolean contains(Object rowKey, Object columnKey) {
synchronized (mutex) {
return this.table.contains(rowKey, columnKey);
}
}
@Override
public boolean containsRow(Object rowKey) {
synchronized (mutex) {
return this.table.containsRow(rowKey);
}
}
@Override
public boolean containsColumn(Object columnKey) {
synchronized (mutex) {
return this.table.containsColumn(columnKey);
}
}
@Override
public boolean containsValue(Object value) {
synchronized (mutex) {
return this.table.containsValue(value);
}
}
@Override
public V get(Object rowKey, Object columnKey) {
synchronized (mutex) {
return this.table.get(rowKey, columnKey);
}
}
@Override
public boolean isEmpty() {
synchronized (mutex) {
return this.table.isEmpty();
}
}
@Override
public int size() {
synchronized (mutex) {
return this.table.size();
}
}
@Override
public void clear() {
synchronized (mutex) {
this.table.clear();
}
}
@Override
public V put(R rowKey, C columnKey, V value) {
synchronized (mutex) {
return this.table.put(rowKey, columnKey, value);
}
}
@Override
public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
synchronized (mutex) {
this.table.putAll(table);
}
}
@Override
public V remove(Object rowKey, Object columnKey) {
synchronized (mutex) {
return this.table.remove(rowKey, columnKey);
}
}
@Override
public Map<C, V> row(R rowKey) {
synchronized (mutex) {
return this.table.row(rowKey);
}
}
@Override
public Map<R, V> column(C columnKey) {
synchronized (mutex) {
return this.table.column(columnKey);
}
}
@Override
public Set<Cell<R, C, V>> cellSet() {
synchronized (mutex) {
return this.table.cellSet();
}
}
@Override
public Set<R> rowKeySet() {
synchronized (mutex) {
return this.table.rowKeySet();
}
}
@Override
public Set<C> columnKeySet() {
synchronized (mutex) {
return this.table.columnKeySet();
}
}
@Override
public Collection<V> values() {
synchronized (mutex) {
return this.table.values();
}
}
@Override
public Map<R, Map<C, V>> rowMap() {
synchronized (mutex) {
return this.table.rowMap();
}
}
@Override
public Map<C, Map<R, V>> columnMap() {
synchronized (mutex) {
return this.table.columnMap();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -21,13 +21,11 @@ import java.util.regex.Pattern;
/**
* 正则表达式常量
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public final class PatternConsts {
// TODO 【添加】 新增身份证等正则常量
public static final Pattern DATE = Pattern.compile(RegexConsts.DATE); // TODO 【优化】 修改为对应的日期格式名称
public static final Pattern DATE = Pattern.compile(RegexConsts.DATE);
public static final Pattern PASSWORD = Pattern.compile(RegexConsts.PASSWORD);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-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.
@@ -19,15 +19,11 @@ package xyz.zhouxy.plusone.commons.constant;
/**
* 正则表达式常量
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public final class RegexConsts {
// TODO 【优化】 根据需要添加 group
// TODO 【添加】 新增身份证等正则常量
public static final String DATE = "^\\d{4}-\\d{2}-\\d{2}"; // TODO 【优化】 修改为对应的日期格式名称
public static final String DATE = "^\\d{4}-\\d{2}-\\d{2}";
public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])[\\w\\\\!#$%&'*\\+\\-/=?^`{|}~@\\(\\)\\[\\]\",\\.;':><]{8,32}$";

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.domain;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.base.Preconditions;
import xyz.zhouxy.plusone.commons.util.RegexUtil;
/**
* 带校验的字符串值对象
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
*/
public abstract class ValidatableStringRecord {
private final String value;
protected ValidatableStringRecord(String value, Pattern pattern) {
Preconditions.checkNotNull(pattern, "The pattern must not be null.");
Preconditions.checkArgument(StringUtils.isNotBlank(value), "The value must be has text.");
Preconditions.checkArgument(RegexUtil.matches(value, pattern));
this.value = value;
}
/**
* 值对象的字符串值。
*
* @return 字符串(不为空)
*/
@JsonValue
public final String value() {
return this.value;
}
@Override
public String toString() {
return this.value();
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.exception;
import xyz.zhouxy.plusone.commons.base.IWithCode;
import javax.annotation.Nonnull;
import java.util.Objects;
/**
* 带错误码的异常。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public abstract class BaseException
extends RuntimeException
implements IWithCode<String> {
private static final long serialVersionUID = -2546365325001947203L;
@Nonnull
private final String code;
protected BaseException(String code, String msg) {
super(msg);
this.code = Objects.requireNonNull(code);
}
protected BaseException(String code, Throwable cause) {
super(cause);
this.code = Objects.requireNonNull(code);
}
protected BaseException(String code, String msg, Throwable cause) {
super(msg, cause);
this.code = Objects.requireNonNull(code);
}
@Nonnull
@Override
public final String getCode() {
return this.code;
}
}

View File

@@ -1,109 +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.exception;
import java.time.format.DateTimeParseException;
/**
* 解析失败异常
*
* <p>
* 解析失败的不一定是客户传的参数,也可能是其它来源的数据解析失败
* 如果表示用户传参造成的解析失败,可使用 RequestParamsException(Throwable cause)
* 将 ParsingFailureException 包装成 {@link RequestParamsException} 再抛出
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
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);
this.type = type;
}
private ParsingFailureException(Type type, Throwable cause) {
super(cause);
this.type = type;
}
private ParsingFailureException(Type type, String msg, Throwable cause) {
super(msg, cause);
this.type = type;
}
public static ParsingFailureException of(Type type) {
return new ParsingFailureException(type);
}
public static ParsingFailureException of(Type type, String msg) {
return new ParsingFailureException(type, msg);
}
public static ParsingFailureException of(Type type, Throwable e) {
return new ParsingFailureException(type, e);
}
public static ParsingFailureException of(Type type, String msg, Throwable e) {
return new ParsingFailureException(type, msg, e);
}
public static ParsingFailureException of(DateTimeParseException e) {
return new ParsingFailureException(Type.DATE_TIME_PARSING_FAILURE, e.getMessage(), e);
}
public static ParsingFailureException of(String msg, DateTimeParseException e) {
return new ParsingFailureException(Type.DATE_TIME_PARSING_FAILURE, msg, e);
}
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 解析失败"),
;
final String code;
final String defaultMsg;
Type(String code, String defaultMsg) {
this.code = code;
this.defaultMsg = defaultMsg;
}
public String getCode() {
return code;
}
public String getDefaultMsg() {
return defaultMsg;
}
}
}

View File

@@ -1,52 +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.exception.business;
/**
* BizException
*
* <p>
* 业务异常
* </p>
*
* <p>
* <b>NOTE: 通常表示业务中的意外情况。如:用户错误输入、缺失必填字段、用户余额不足等。</b>
* </p>
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public class BizException extends RuntimeException {
private static final String DEFAULT_MSG = "业务异常";
public BizException() {
super(DEFAULT_MSG);
}
public BizException(String msg) {
super(msg);
}
public BizException(Throwable cause) {
super(cause);
}
public BizException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,108 +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.exception.business;
/**
* InvalidInputException
*
* <p>
* 用户输入内容非法
* </p>
*
* <p>
* <b>NOTE: 属业务异常</b>
* </p>
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public final class InvalidInputException extends RequestParamsException {
private final Type type;
private InvalidInputException(Type type) {
super(type.getDefaultMsg());
this.type = type;
}
private InvalidInputException(Type type, String msg) {
super(msg);
this.type = type;
}
private InvalidInputException(Type type, Throwable cause) {
super(cause);
this.type = type;
}
private InvalidInputException(Type type, String msg, Throwable cause) {
super(msg, cause);
this.type = type;
}
public static InvalidInputException of(Type type) {
return new InvalidInputException(type);
}
public static InvalidInputException of(Type type, String msg) {
return new InvalidInputException(type, msg);
}
public static InvalidInputException of(Type type, Throwable e) {
return new InvalidInputException(type, e);
}
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 Type getType() {
return type;
}
public enum Type {
DEFAULT("00", "用户输入内容非法"),
CONTAINS_ILLEGAL_AND_MALICIOUS_LINKS("01", "包含非法恶意跳转链接"),
CONTAINS_ILLEGAL_WORDS("02", "包含违禁敏感词"),
PICTURE_CONTAINS_ILLEGAL_INFORMATION("03", "图片包含违禁信息"),
INFRINGE_COPYRIGHT("04", "文件侵犯版权"),
;
final String code;
final String defaultMsg;
Type(String code, String defaultMsg) {
this.code = code;
this.defaultMsg = defaultMsg;
}
public String getCode() {
return code;
}
public String getDefaultMsg() {
return defaultMsg;
}
}
}

View File

@@ -1,49 +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.exception.business;
/**
* RequestParamsException
*
* <p>
* 用户请求参数错误
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public class RequestParamsException extends BizException {
private static final String DEFAULT_MSG = "用户请求参数错误";
public RequestParamsException() {
super(DEFAULT_MSG);
}
public RequestParamsException(String msg) {
super(msg);
}
public RequestParamsException(Throwable cause) {
super(cause);
}
public RequestParamsException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,53 +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.exception.system;
/**
* DataOperationResultException
*
* <p>
* 当数据操作的结果不符合预期时抛出。
* </p>
*
* <p>
* 比如当一个 insert 或 update 操作时,预计影响数据库中的一行数据,但结果却影响了零条数据或多条数据,
* 当出现这种始料未及的诡异情况时,抛出 {@link DataOperationResultException} 并回滚事务。
* 后续需要排查原因。
* </p>
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public final class DataOperationResultException extends SysException {
private static final String DEFAULT_MSG = "数据操作的结果不符合预期";
public DataOperationResultException() {
super(DEFAULT_MSG);
}
public DataOperationResultException(String msg) {
super(msg);
}
public DataOperationResultException(Throwable cause) {
super(cause);
}
public DataOperationResultException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -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.exception.system;
/**
* NoAvailableMacFoundException
*
* <p>
* 在无法找到可访问的 Mac 地址时抛出
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public class NoAvailableMacFoundException extends SysException {
private static final long serialVersionUID = 152827098461071551L;
public NoAvailableMacFoundException() {
super();
}
public NoAvailableMacFoundException(String msg) {
super(msg);
}
public NoAvailableMacFoundException(Throwable e) {
super(e);
}
public NoAvailableMacFoundException(String msg, Throwable e) {
super(msg, e);
}
}

View File

@@ -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.exception.system;
/**
* 系统异常
*
* <p>
* 通常表示应用代码存在问题,或因环境问题,引发异常。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public class SysException extends RuntimeException {
private static final String DEFAULT_MSG = "系统异常";
protected SysException() {
super(DEFAULT_MSG);
}
public SysException(String msg) {
super(msg);
}
public SysException(Throwable cause) {
super(cause);
}
public SysException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.function;
import java.util.Optional;
import java.util.function.DoubleFunction;
/**
* DoubleToOptionalFunction
*
* <p>
* 接受类型为 double 的参数,返回 {@code Optional&lt;R&gt;} 对象。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Optional
* @see DoubleFunction
*/
@FunctionalInterface
public interface DoubleToOptionalFunction<R> extends DoubleFunction<Optional<R>> {
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.function;
import java.util.Optional;
import java.util.function.IntFunction;
/**
* IntToOptionalFunction
*
* <p>
* 接受类型为 int 的参数,返回 {@code Optional&lt;R&gt;} 对象。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Optional
* @see IntFunction
*/
@FunctionalInterface
public interface IntToOptionalFunction<R> extends IntFunction<Optional<R>> {
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.function;
import java.util.Optional;
import java.util.function.LongFunction;
/**
* LongToOptionalFunction
*
* <p>
* 接受类型为 long 的参数,返回 {@code Optional&lt;R&gt;} 对象。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Optional
* @see LongFunction
*/
@FunctionalInterface
public interface LongToOptionalFunction<R> extends LongFunction<Optional<R>> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2022-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.
@@ -16,14 +16,20 @@
package xyz.zhouxy.plusone.commons.function;
import java.util.OptionalDouble;
import java.util.function.Supplier;
/**
* OptionalDoubleSupplier
*
* <p>
* 返回 {@link OptionalDouble} 对象
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see OptionalDouble
* @see Supplier
*/
@FunctionalInterface
public interface ThrowingConsumer<T, E extends Throwable> {
/**
* Consume the supplied argument, potentially throwing an exception.
*
* @param t the argument to consume
*/
void accept(T t) throws E;
public interface OptionalDoubleSupplier extends Supplier<OptionalDouble> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2022-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.
@@ -16,9 +16,20 @@
package xyz.zhouxy.plusone.commons.function;
import java.util.OptionalInt;
import java.util.function.Supplier;
/**
* OptionalIntSupplier
*
* <p>
* 返回 {@link OptionalInt} 对象
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see OptionalInt
* @see Supplier
*/
@FunctionalInterface
public interface Executable<E extends Throwable> {
void execute() throws E;
public interface OptionalIntSupplier extends Supplier<OptionalInt> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2022-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.
@@ -16,10 +16,20 @@
package xyz.zhouxy.plusone.commons.function;
import com.google.common.annotations.Beta;
import java.util.OptionalLong;
import java.util.function.Supplier;
@Beta
/**
* OptionalLongSupplier
*
* <p>
* 返回 {@link OptionalLong} 对象
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see OptionalLong
* @see Supplier
*/
@FunctionalInterface
public interface CharUnaryOperator {
char applyAsChar(char operand);
public interface OptionalLongSupplier extends Supplier<OptionalLong> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -25,7 +25,7 @@ import java.util.function.Supplier;
* <p>
* 返回 {@code Optional&lt;T&gt;} 对象。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Optional
* @see Supplier

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -25,11 +25,11 @@ import java.util.function.Predicate;
* {@link Predicate} 相关操作
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Predicate
*/
public class PredicateTools {
public class Predicates {
/**
* lambda 表达式或者方法引用指明为对应类型的 {@link Predicate} 对象
@@ -38,19 +38,19 @@ public class PredicateTools {
* 等方法连接其它 {@code Predicate<? super T>} 对象
*
* <pre>
* Predicate&lt;String&gt; predicate = PredicateTools.&lt;String&gt;from(Objects::nonNull)
* Predicate&lt;String&gt; predicate = Predicates.&lt;String&gt;of(Objects::nonNull)
* .and(StringUtils::isNotEmpty);
* </pre>
*
* @param <T> 目标类型
* @param predicate Lambda 表达式
* @return 传入的表达式自动成为 {@link Predicate} 实例
* @param predicate {@link Predicate} 实例
* @return 包装的 {@link Predicate} 实例
*/
public static <T> Predicate<T> from(Predicate<T> predicate) {
return predicate;
public static <T> Predicate<T> of(Predicate<? super T> predicate) {
return predicate::test;
}
private PredicateTools() {
private Predicates() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,29 +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.function;
public interface ThrowingPredicate<T, E extends Throwable> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t) throws E;
}

View File

@@ -1,29 +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.function;
@FunctionalInterface
public interface ThrowingSupplier<T, E extends Throwable> {
/**
* Get a result, potentially throwing an exception.
*
* @return a result
*/
T get() throws E;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -25,7 +25,7 @@ import java.util.function.BiFunction;
* <p>
* 接受类型为 T 和 U 的两个参数,返回 {@code Optional&lt;R&gt;} 对象。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Optional
* @see BiFunction

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.function;
import java.util.OptionalDouble;
import java.util.function.Function;
/**
* ToOptionalDoubleFunction
*
* <p>
* 接受类型为 T 的参数,返回 {@link OptionalDouble} 对象。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see OptionalDouble
* @see Function
*/
@FunctionalInterface
public interface ToOptionalDoubleFunction<T> extends Function<T, OptionalDouble> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -25,7 +25,7 @@ import java.util.function.Function;
* <p>
* 接受类型为 T 的参数,返回 {@code Optional&lt;R&gt;} 对象。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Optional
* @see Function

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2022-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.
@@ -16,14 +16,20 @@
package xyz.zhouxy.plusone.commons.function;
import com.google.common.annotations.Beta;
import java.util.OptionalInt;
import java.util.function.Function;
@Beta
/**
* ToOptionalIntFunction
*
* <p>
* 接受类型为 T 的参数返回 {@link OptionalInt} 对象
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see OptionalInt
* @see Function
*/
@FunctionalInterface
public interface BoolUnaryOperator {
boolean applyAsBool(boolean operand);
static BoolUnaryOperator not() {
return b -> !b;
}
public interface ToOptionalIntFunction<T> extends Function<T, OptionalInt> {
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.function;
import java.util.OptionalLong;
import java.util.function.Function;
/**
* ToOptionalLongFunction
*
* <p>
* 接受类型为 T 的参数,返回 {@link OptionalLong} 对象。
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see OptionalLong
* @see Function
*/
@FunctionalInterface
public interface ToOptionalLongFunction<T> extends Function<T, OptionalLong> {
}

View File

@@ -1,168 +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.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;
/**
* 中国第二代居民身份证号
*/
public class Chinese2ndGenIDCardNumber extends IDCardNumber {
/** 省份编码 */
private final String provinceCode;
/** 市级编码 */
private final String cityCode;
/** 县级编码 */
private final String countyCode;
/** 性别 */
private final Gender gender;
/** 出生日期 */
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");
try {
final Matcher matcher = getMatcher();
this.provinceCode = matcher.group(3);
this.cityCode = matcher.group(2);
this.countyCode = matcher.group(1);
// 性别
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);
}
catch (DateTimeParseException e) {
throw new IllegalArgumentException(e);
}
}
public static Chinese2ndGenIDCardNumber of(String idNumber) {
return new Chinese2ndGenIDCardNumber(idNumber);
}
public String getProvinceCode() {
return provinceCode;
}
public String getProvinceName() {
return PROVINCE_CODES.get(this.provinceCode);
}
public String getFullProvinceCode() {
return Strings.padEnd(this.provinceCode, 12, '0');
}
public String getCityCode() {
return cityCode;
}
public String getFullCityCode() {
return Strings.padEnd(this.cityCode, 12, '0');
}
public String getCountyCode() {
return countyCode;
}
public String getFullCountyCode() {
return Strings.padEnd(this.countyCode, 12, '0');
}
@Override
public Gender getGender() {
return gender;
}
@Override
public LocalDate getBirthDate() {
return birthDate;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
/**
* 省份代码表
*/
public static final Map<String, String> PROVINCE_CODES;
static {
PROVINCE_CODES = ImmutableMap.<String, String>builder()
.put("11", "北京")
.put("12", "天津")
.put("13", "河北")
.put("14", "山西")
.put("15", "内蒙古")
.put("21", "辽宁")
.put("22", "吉林")
.put("23", "黑龙江")
.put("31", "上海")
.put("32", "江苏")
.put("33", "浙江")
.put("34", "安徽")
.put("35", "福建")
.put("36", "江西")
.put("37", "山东")
.put("41", "河南")
.put("42", "湖北")
.put("43", "湖南")
.put("44", "广东")
.put("45", "广西")
.put("46", "海南")
.put("50", "重庆")
.put("51", "四川")
.put("52", "贵州")
.put("53", "云南")
.put("54", "西藏")
.put("61", "陕西")
.put("62", "甘肃")
.put("63", "青海")
.put("64", "宁夏")
.put("65", "新疆")
.put("71", "台湾")
.put("81", "香港")
.put("82", "澳门")
.put("83", "台湾") // 台湾身份证号码以83开头但是行政区划为71
.put("91", "国外")
.build();
}
}

View File

@@ -1,63 +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.model;
import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
* 性别
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public enum Gender {
UNKNOWN(0, "Unknown", "未知"),
MALE(1, "Male", ""),
FEMALE(2, "Female", ""),
;
private static final Gender[] VALUES = new Gender[] { UNKNOWN, MALE, FEMALE };
private final int value;
private final String displayName;
private final String displayNameZh;
Gender(int value, String displayName, String displayNameZh) {
this.value = value;
this.displayName = displayName;
this.displayNameZh = displayNameZh;
}
public static Gender of(int value) {
AssertTools.checkCondition(0 <= value && value < VALUES.length,
() -> new EnumConstantNotPresentException(Gender.class, String.valueOf(value)));
return VALUES[value];
}
public int getValue() {
return value;
}
public String getDisplayName() {
return displayName;
}
public String getDisplayNameZh() {
return displayNameZh;
}
}

View File

@@ -1,61 +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.model;
import java.time.LocalDate;
import java.time.Period;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
/**
* 身份证号
*/
public abstract class IDCardNumber extends ValidatableStringRecord {
protected IDCardNumber(@Nonnull String idNumber, @Nonnull Pattern pattern)
throws IllegalArgumentException{
super(idNumber, pattern);
}
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<String> errorMessage) {
super(idNumber, pattern, errorMessage);
}
/**
* 根据身份证号判断性别
*/
public abstract Gender getGender();
/**
* 获取出生日期
*/
public abstract LocalDate getBirthDate();
/** 计算年龄 */
public final int calculateAge() {
LocalDate now = LocalDate.now();
return Period.between(getBirthDate(), now).getYears();
}
}

View File

@@ -1,100 +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.model;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import xyz.zhouxy.plusone.commons.util.AssertTools;
/**
* 带校验的字符串值对象
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public abstract class ValidatableStringRecord
implements Comparable<ValidatableStringRecord> {
@Nonnull
private final String value;
private final Matcher matcher;
protected ValidatableStringRecord(@Nonnull String value, @Nonnull Pattern pattern) {
this(value, pattern, "Invalid value");
}
protected ValidatableStringRecord(@Nonnull String value, @Nonnull Pattern pattern,
@Nonnull Supplier<String> errorMessageSupplier) {
this(value, pattern, errorMessageSupplier.get());
}
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.");
this.matcher = pattern.matcher(value);
AssertTools.checkArgument(this.matcher.matches(), errorMessage);
this.value = value;
}
/**
* 值对象的字符串值。
*
* @return 字符串(不为空)
*/
public final String value() {
return this.value;
}
@Override
public int compareTo(ValidatableStringRecord o) {
return this.value.compareTo(o.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ValidatableStringRecord other = (ValidatableStringRecord) obj;
return Objects.equals(value, other.value);
}
@Override
public String toString() {
return this.value();
}
protected final Matcher getMatcher() {
return matcher;
}
}

View File

@@ -1,158 +0,0 @@
/*
* Copyright 2022-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 java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
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.util.StringTools;
/**
* 分页排序查询参数
*
* <p>
* 根据传入的 {@code size} 和 {@code pageNum}
* 提供 {@code getOffset} 方法计算 SQL 语句中 {@code offset} 的值。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @see PageResult
*/
public class PagingAndSortingQueryParams {
private static final int DEFAULT_PAGE_SIZE = 15;
private Integer size;
private Long pageNum;
private List<String> orderBy;
private static final Pattern sortStrPattern = Pattern.compile("^[a-zA-Z]\\w+-(desc|asc|DESC|ASC)$");
private final Map<String, String> sortableProperties;
public PagingAndSortingQueryParams(Map<String, String> sortableProperties) {
Preconditions.checkArgument(sortableProperties != null && !sortableProperties.isEmpty(),
"Sortable properties can not be empty.");
sortableProperties.forEach((k, v) ->
Preconditions.checkArgument(StringTools.isNotBlank(k) && StringTools.isNotBlank(v),
"Property name must not be blank."));
this.sortableProperties = ImmutableMap.copyOf(sortableProperties);
}
// Setters
public final void setOrderBy(@Nullable List<String> orderBy) {
this.orderBy = orderBy;
}
public final void setSize(@Nullable Integer size) {
this.size = size;
}
public final void setPageNum(@Nullable Long pageNum) {
this.pageNum = pageNum;
}
// Setters end
public final PagingParams buildPagingParams() {
final int sizeValue = this.size != null ? this.size : defaultSizeInternal();
final long pageNumValue = this.pageNum != null ? this.pageNum : 1L;
final List<SortableProperty> propertiesToSort = this.orderBy.stream().map(this::generateSortableProperty)
.collect(Collectors.toList());
return new PagingParams(sizeValue, pageNumValue, propertiesToSort);
}
@Virtual
protected int defaultSizeInternal() {
return DEFAULT_PAGE_SIZE;
}
@Override
public String toString() {
return "PagingAndSortingQueryParams ["
+ "size=" + size
+ ", pageNum=" + pageNum
+ ", orderBy=" + orderBy
+ ", sortableProperties=" + sortableProperties
+ "]";
}
private SortableProperty generateSortableProperty(String orderByStr) {
Preconditions.checkArgument(PagingAndSortingQueryParams.sortStrPattern.matcher(orderByStr).matches());
String[] propertyNameAndOrderType = orderByStr.split("-");
Preconditions.checkArgument(propertyNameAndOrderType.length == 2);
String propertyName = propertyNameAndOrderType[0];
Preconditions.checkArgument(sortableProperties.containsKey(propertyName),
"The property name must be in the set of sortable properties.");
String columnName = sortableProperties.get(propertyName);
String orderType = propertyNameAndOrderType[1];
return new SortableProperty(propertyName, columnName, orderType);
}
public static final class SortableProperty {
private final String propertyName;
private final String columnName;
private final String orderType;
private final String sqlSnippet;
SortableProperty(String propertyName, String columnName, String orderType) {
this.propertyName = propertyName;
this.columnName = columnName;
Preconditions.checkArgument("ASC".equalsIgnoreCase(orderType) || "DESC".equalsIgnoreCase(orderType));
this.orderType = orderType.toUpperCase();
this.sqlSnippet = this.propertyName + " " + this.orderType;
}
public String getPropertyName() {
return propertyName;
}
public String getColumnName() {
return columnName;
}
public String getOrderType() {
return orderType;
}
public String getSqlSnippet() {
return sqlSnippet;
}
@Override
public String toString() {
return "SortableProperty ["
+ "propertyName=" + propertyName
+ ", columnName=" + columnName
+ ", orderType=" + orderType
+ "]";
}
}
}

View File

@@ -1,63 +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.model.dto;
import java.util.Collections;
import java.util.List;
import xyz.zhouxy.plusone.commons.model.dto.PagingAndSortingQueryParams.SortableProperty;
public class PagingParams {
private final int size;
private final long pageNum;
private final long offset;
private final List<SortableProperty> orderBy;
PagingParams(int size, long pageNum, List<SortableProperty> orderBy) {
this.size = size;
this.pageNum = pageNum;
this.offset = (pageNum - 1) * size;
this.orderBy = orderBy;
}
// Getters
public final List<SortableProperty> getOrderBy() {
return Collections.unmodifiableList(this.orderBy);
}
public final int getSize() {
return this.size;
}
public final long getPageNum() {
return this.pageNum;
}
public final long getOffset() {
return this.offset;
}
// Getters end
@Override
public String toString() {
return "PageInfo [size=" + size + ", pageNum=" + pageNum + ", orderBy=" + orderBy + ", offset="
+ getOffset() + "]";
}
}

View File

@@ -1,209 +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.model.dto;
import java.util.Objects;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
/**
* 统一结果,对返回给前端的数据进行封装。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public abstract class UnifiedResponse {
private Object status;
private String message;
private @Nullable Object data;
public static UnifiedResponse success() {
return new SuccessResult();
}
public static UnifiedResponse success(@Nullable String message) {
return new SuccessResult(message);
}
public static UnifiedResponse success(@Nullable String message, @Nullable Object data) {
return new SuccessResult(message, data);
}
public static UnifiedResponse error(@Nullable String message) {
return new ErrorResult(message);
}
public static UnifiedResponse error(@Nullable String message, @Nullable Object data) {
return new ErrorResult(message, data);
}
public static UnifiedResponse error(Object status, @Nullable String message) {
return new ErrorResult(status, message);
}
public static UnifiedResponse error(Object status, @Nullable String message, @Nullable Object data) {
return new ErrorResult(status, message, data);
}
public static UnifiedResponse error(Object status, Throwable e) {
return new ErrorResult(status, e);
}
public static UnifiedResponse of(Object status, @Nullable String message) {
return new CustomResult(status, message);
}
public static UnifiedResponse of(Object status, @Nullable String message, @Nullable Object data) {
return new CustomResult(status, message, data);
}
public static UnifiedResponse of(final boolean isSuccess,
final Supplier<SuccessResult> successResult, final Supplier<ErrorResult> errorResult) {
Preconditions.checkNotNull(successResult, "Success supplier must not be null.");
Preconditions.checkNotNull(errorResult, "Error supplier must not be null.");
return isSuccess ? successResult.get() : errorResult.get();
}
public static UnifiedResponse of(final BooleanSupplier isSuccess,
final Supplier<SuccessResult> successResult, final Supplier<ErrorResult> 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.");
return isSuccess.getAsBoolean() ? successResult.get() : errorResult.get();
}
protected UnifiedResponse(Object status, @Nullable String message) {
setStatus(status);
setMessage(message);
}
protected UnifiedResponse(Object status, @Nullable String message, @Nullable Object data) {
setStatus(status);
setMessage(message);
setData(data);
}
private void setStatus(Object status) {
this.status = Objects.requireNonNull(status);
}
private void setMessage(@Nullable String message) {
this.message = message == null ? "" : message;
}
private void setData(@Nullable Object data) {
this.data = data;
}
// Constructors end
// Getters
public Object getStatus() {
return status;
}
public String getMessage() {
return message;
}
@Nullable
public Object getData() {
return data;
}
// Getters end
@Override
public String toString() {
return String.format("{status: %s, message: \"%s\", data: %s}",
transValue(this.status), this.message, transValue(this.data));
}
private static String transValue(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return "\"" + value + "\"";
}
return String.valueOf(value);
}
protected static class SuccessResult extends UnifiedResponse {
public static final String SUCCESS_STATUS = "2000000";
private static final String DEFAULT_SUCCESS_MSG = "SUCCESS";
SuccessResult() {
super(SUCCESS_STATUS, DEFAULT_SUCCESS_MSG);
}
SuccessResult(@Nullable String message) {
super(SUCCESS_STATUS, message);
}
SuccessResult(@Nullable String message, @Nullable Object data) {
super(SUCCESS_STATUS, message, data);
}
}
protected static class ErrorResult extends UnifiedResponse {
public static final String DEFAULT_ERROR_STATUS = "9999999";
ErrorResult(@Nullable String message) {
super(DEFAULT_ERROR_STATUS, message);
}
ErrorResult(@Nullable String message, @Nullable Object data) {
super(DEFAULT_ERROR_STATUS, message, data);
}
ErrorResult(Object status, @Nullable String message) {
super(status, message);
}
ErrorResult(Object status, @Nullable String message, @Nullable Object data) {
super(status, message, data);
}
ErrorResult(Object status, Throwable e) {
super(status, Objects.requireNonNull(e).getMessage());
}
}
/**
* 自定义结果
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
protected static class CustomResult extends UnifiedResponse {
CustomResult(Object status, @Nullable String message) {
super(status, message);
}
CustomResult(Object status, @Nullable String message, @Nullable Object data) {
super(status, message, data);
}
}
}

View File

@@ -0,0 +1,58 @@
package xyz.zhouxy.plusone.commons.net;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import com.google.common.annotations.Beta;
@Beta
public class MoreInetAddresses {
public static NetworkInterfaceInfo getNetworkInterfaceInfo() throws SocketException {
final Enumeration<NetworkInterface> all = NetworkInterface.getNetworkInterfaces();
NetworkInterface networkInterface;
while (all.hasMoreElements()) {
networkInterface = all.nextElement();
if (networkInterface.isLoopback() || networkInterface.isVirtual()) {
continue;
}
final byte[] mac = networkInterface.getHardwareAddress();
if (mac != null) {
return new NetworkInterfaceInfo(networkInterface, mac);
}
}
throw new IllegalStateException("No available network interface found");
}
public static Inet4Address getIpv4(NetworkInterface networkInterface) {
final Enumeration<InetAddress> ips = networkInterface.getInetAddresses();
InetAddress ip;
while (ips.hasMoreElements()) {
ip = ips.nextElement();
if (!ip.isLoopbackAddress() && (ip instanceof Inet4Address)) {
return (Inet4Address) ip;
}
}
throw new IllegalStateException("No available address found");
}
public static Inet6Address getIpv6(NetworkInterface networkInterface) {
final Enumeration<InetAddress> ips = networkInterface.getInetAddresses();
InetAddress ip;
while (ips.hasMoreElements()) {
ip = ips.nextElement();
if (!ip.isLoopbackAddress() && (ip instanceof Inet6Address)) {
return (Inet6Address) ip;
}
}
throw new IllegalStateException("No available address found");
}
private MoreInetAddresses() {
throw new UnsupportedOperationException("Utility class");
}
}

View File

@@ -0,0 +1,39 @@
package xyz.zhouxy.plusone.commons.net;
import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.Objects;
public class NetworkInterfaceInfo {
private final NetworkInterface networkInterface;
private final byte[] mac;
NetworkInterfaceInfo(NetworkInterface networkInterface, byte[] mac) {
this.networkInterface = Objects.requireNonNull(networkInterface);
this.mac = Objects.requireNonNull(mac);
}
public NetworkInterface getNetworkInterface() {
return networkInterface;
}
public byte[] getMac() {
return Arrays.copyOf(this.mac, this.mac.length);
}
public String getMacStr() {
final StringBuilder result = new StringBuilder();
String s;
for (int i = 0; i < this.mac.length; i++) {
if (i != 0) {
result.append("-");
}
s = Integer.toHexString(this.mac[i] & 0xFF);
if (s.length() == 1) {
result.append("0");
}
result.append(s);
}
return result.toString();
}
}

View File

@@ -1,19 +1,3 @@
/*
* 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;
@@ -69,4 +53,4 @@ public class JdbcSql extends SQL<JdbcSql> {
}
return arr;
}
}
}

View File

@@ -1,19 +1,3 @@
/*
* 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;
@@ -42,14 +26,14 @@ public class MyBatisSql extends SQL<MyBatisSql> {
}
public static String IN(String col, String paramName) {
return " " + col + " IN" + buildForeach(col, paramName);
return " " + col + " IN" + buildQuestionsList(col, paramName);
}
public static String NOT_IN(String col, String paramName) {
return col + " NOT IN" + buildForeach(col, paramName);
return col + " NOT IN" + buildQuestionsList(col, paramName);
}
private static String buildForeach(String col, String paramName) {
private static String buildQuestionsList(String col, String paramName) {
final String format = "<foreach" +
" item=\"%s\"" +
" index=\"index\"" +
@@ -70,4 +54,4 @@ public class MyBatisSql extends SQL<MyBatisSql> {
}
return super.toString();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -21,7 +21,7 @@ import org.apache.ibatis.jdbc.AbstractSQL;
import com.google.common.annotations.Beta;
/**
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author ZhouXY
*/
@Beta
public abstract class SQL<T> extends AbstractSQL<T> {
@@ -44,4 +44,4 @@ public abstract class SQL<T> extends AbstractSQL<T> {
public T WHERE(boolean condition, String ifSqlCondition, String elseSqlCondition) {
return WHERE(condition ? ifSqlCondition : elseSqlCondition);
}
}
}

View File

@@ -1,180 +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.time;
import java.time.Month;
import java.time.MonthDay;
import java.time.temporal.ChronoField;
import com.google.common.collect.Range;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
/**
* 季度
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public enum Quarter {
/** 第一季度 */
Q1(1),
/** 第二季度 */
Q2(2),
/** 第三季度 */
Q3(3),
/** 第四季度 */
Q4(4),
;
/** 季度值 (1/2/3/4) */
private final int value;
private final Range<Integer> monthRange;
/** 常量值 */
private static final Quarter[] ENUMS = Quarter.values();
/**
* @param value 季度值 (1/2/3/4)
*/
Quarter(int value) {
this.value = value;
final int lastMonth = value * 3;
final int firstMonth = lastMonth - 2;
this.monthRange = Range.closed(firstMonth, lastMonth);
}
// StaticFactoryMethods
/**
* 根据给定的月份值返回对应的季度
*
* @param monthValue 月份值取值范围为1到12
* @return 对应的季度
* @throws IllegalArgumentException 如果月份值不在有效范围内1到12将抛出异常
*/
@StaticFactoryMethod(Quarter.class)
public static Quarter fromMonth(int monthValue) {
ChronoField.MONTH_OF_YEAR.checkValidValue(monthValue);
return of(computeQuarterValueInternal(monthValue));
}
/**
* 根据给定的月份返回对应的季度
*
* @param month 月份
* @return 对应的季度
*/
@StaticFactoryMethod(Quarter.class)
public static Quarter fromMonth(Month month) {
final int monthValue = month.getValue();
return of(computeQuarterValueInternal(monthValue));
}
/**
* 根据指定的年份,获取一个新的 YearQuarter 实例
* 此方法允许在保持当前季度信息不变的情况下,更改年份
*
* @param year 指定的年份
* @return 返回一个新的 YearQuarter 实例,年份更新为指定的年份
*/
public final YearQuarter atYear(int year) {
return YearQuarter.of(year, this);
}
/**
* 根据给定的季度值返回对应的季度
*
* @param value 季度值 (1/2/3/4)
* @return 对应的季度
* @throws IllegalArgumentException 如果季度值不在有效范围内1到4将抛出异常
*/
@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];
}
// StaticFactoryMethods end
// computs
public Quarter plus(long quarters) { // TODO 单元测试
final int amount = (int) ((quarters % 4) + 4);
return ENUMS[(ordinal() + amount) % 4];
}
public Quarter minus(long quarters) { // TODO 单元测试
return plus(-(quarters % 4));
}
// computs end
// Getters
public int getValue() {
return value;
}
public Month firstMonth() {
return Month.of(firstMonthValue());
}
public int firstMonthValue() {
return this.monthRange.lowerEndpoint();
}
public Month lastMonth() {
return Month.of(lastMonthValue());
}
public int lastMonthValue() {
return this.monthRange.upperEndpoint();
}
public MonthDay firstMonthDay() {
return MonthDay.of(firstMonth(), 1);
}
public MonthDay lastMonthDay() {
// 季度的最后一个月不可能是 2 月
final Month month = lastMonth();
return MonthDay.of(month, month.maxLength());
}
public int firstDayOfYear(boolean leapYear) {
return firstMonth().firstDayOfYear(leapYear);
}
// Getters end
// Internal
/**
* 计算给定月份对应的季度值
*
* @param monthValue 月份值取值范围为1到12
* @return 对应的季度值
*/
private static int computeQuarterValueInternal(int monthValue) {
return (monthValue - 1) / 3 + 1;
}
}

View File

@@ -1,268 +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.time;
import static java.time.temporal.ChronoField.YEAR;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.Month;
import java.time.YearMonth;
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;
/**
* 表示年份与季度
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
@Immutable
public final class YearQuarter implements Comparable<YearQuarter>, Serializable {
private static final long serialVersionUID = 3804145964419489753L;
/** 年份 */
private final int year;
/** 季度 */
private final Quarter quarter;
/** 季度开始日期 */
private final LocalDate firstDate;
/** 季度结束日期 */
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
/**
* 根据指定年份与季度,创建 {@link YearQuarter} 实例
*
* @param year 年份
* @param quarter 季度
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(int year, int quarter) {
return of(year, Quarter.of(quarter));
}
/**
* 根据指定年份与季度,创建 {@link YearQuarter} 实例
*
* @param year 年份
* @param quarter 季度
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(int year, @Nonnull Quarter quarter) {
return new YearQuarter(year, quarter);
}
/**
* 根据指定日期,判断日期所在的年份与季度,创建 {@link YearQuarter} 实例
*
* @param date 日期
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(@Nonnull LocalDate date) {
return of(date.getYear(), Quarter.fromMonth(date.getMonth()));
}
/**
* 根据指定日期,判断日期所在的年份与季度,创建 {@link YearQuarter} 实例
*
* @param date 日期
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(@Nonnull Date date) {
@SuppressWarnings("deprecation")
final int year = date.getYear() + 1900;
@SuppressWarnings("deprecation")
final int month = date.getMonth() + 1;
return of(year, Quarter.fromMonth(month));
}
/**
* 根据指定日期,判断日期所在的年份与季度,创建 {@link YearQuarter} 实例
*
* @param date 日期
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(Calendar date) {
return of(date.get(Calendar.YEAR), Quarter.fromMonth(date.get(Calendar.MONTH) + 1));
}
/**
* 根据指定年月,判断其所在的年份与季度,创建 {@link YearQuarter} 实例
*
* @param yearMonth 年月
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class)
public static YearQuarter of(YearMonth yearMonth) {
return of(yearMonth.getYear(), Quarter.fromMonth(yearMonth.getMonth()));
}
// #endregion
// #region - Getters
public int getYear() {
return year;
}
public Quarter getQuarter() {
return quarter;
}
public YearMonth firstYearMonth() {
return YearMonth.of(this.year, this.quarter.firstMonth());
}
public Month firstMonth() {
return this.quarter.firstMonth();
}
public int firstMonthValue() {
return this.quarter.firstMonthValue();
}
public YearMonth lastYearMonth() {
return YearMonth.of(this.year, this.quarter.lastMonth());
}
public Month lastMonth() {
return this.quarter.lastMonth();
}
public int lastMonthValue() {
return this.quarter.lastMonthValue();
}
public LocalDate firstDate() {
return firstDate;
}
public LocalDate lastDate() {
return lastDate;
}
// #endregion
// #region - computes
public YearQuarter plusQuarters(long quartersToAdd) { // TODO 单元测试
if (quartersToAdd == 0) {
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));
}
public YearQuarter minusQuarters(long quartersToAdd) { // TODO 单元测试
return plusQuarters(-quartersToAdd);
}
public YearQuarter plusYears(long yearsToAdd) { // TODO 单元测试
if (yearsToAdd == 0) {
return this;
}
int newYear = YEAR.checkValidIntValue(this.year + yearsToAdd); // safe overflow
return of(newYear, this.quarter);
}
public YearQuarter minusYears(long yearsToAdd) {
return plusYears(-yearsToAdd);
}
// #endregion
// #region - hashCode & equals
@Override
public int hashCode() {
return Objects.hash(year, quarter);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
YearQuarter other = (YearQuarter) obj;
return year == other.year && quarter == other.quarter;
}
// #endregion
// #region - compareTo
@Override
public int compareTo(YearQuarter other) {
int cmp = (this.year - other.year);
if (cmp == 0) {
cmp = this.quarter.compareTo(other.quarter);
}
return cmp;
}
public boolean isBefore(YearQuarter other) {
return this.compareTo(other) < 0;
}
public boolean isAfter(YearQuarter other) {
return this.compareTo(other) > 0;
}
// #endregion
// #region - toString
/**
* 返回 {@link YearQuarter} 的字符串表示形式,如 "2024 Q3"
*
* @return {@link YearQuarter} 的字符串表示形式
*/
@Override
public String toString() {
return this.year + " " + this.quarter.name();
}
// #endregion
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,135 +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 java.util.function.Supplier;
import javax.annotation.Nonnull;
/**
* 断言工具
*
* <p>
* 本工具类不封装过多判断逻辑,鼓励充分使用项目中的工具类进行逻辑判断。
* </p>
*
* <pre>
* 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");
* </pre>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public class AssertTools {
// #region - checkArgument
public static <T> void checkArgumentNotNull(T argument) {
checkCondition(argument != null, () -> new IllegalArgumentException("The argument cannot be null."));
}
public static <T> void checkArgumentNotNull(T argument, String errMsg) {
checkCondition(argument != null, () -> new IllegalArgumentException(errMsg));
}
public static <T> void checkArgumentNotNull(T argument, Supplier<String> messageSupplier) {
checkCondition(argument != null, () -> new IllegalArgumentException(messageSupplier.get()));
}
public static <T> void checkArgumentNotNull(T argument, String format, Object... args) {
checkCondition(argument != null, () -> new IllegalArgumentException(String.format(format, args)));
}
public static void checkArgument(boolean condition) {
checkCondition(condition, IllegalArgumentException::new);
}
public static void checkArgument(boolean condition, String errMsg) {
checkCondition(condition, () -> new IllegalArgumentException(errMsg));
}
public static void checkArgument(boolean condition, Supplier<String> messageSupplier) {
checkCondition(condition, () -> new IllegalArgumentException(messageSupplier.get()));
}
public static void checkArgument(boolean condition, String format, Object... args) {
checkCondition(condition, () -> new IllegalArgumentException(String.format(format, args)));
}
// #endregion
// #region - checkState
public static void checkState(boolean condition) {
checkCondition(condition, IllegalStateException::new);
}
public static void checkState(boolean condition, String errMsg) {
checkCondition(condition, () -> new IllegalStateException(errMsg));
}
public static void checkState(boolean condition, Supplier<String> messageSupplier) {
checkCondition(condition, () -> new IllegalStateException(messageSupplier.get()));
}
public static void checkState(boolean condition, String format, Object... args) {
checkCondition(condition, () -> new IllegalStateException(String.format(format, args)));
}
// #endregion
// #region - checkNotNull
public static <T> void checkNotNull(T obj) {
checkCondition(obj != null, NullPointerException::new);
}
public static <T> void checkNotNull(T obj, String errMsg) {
checkCondition(obj != null, () -> new NullPointerException(errMsg));
}
public static <T> void checkNotNull(T obj, Supplier<String> messageSupplier) {
checkCondition(obj != null, () -> new NullPointerException(messageSupplier.get()));
}
public static <T> void checkNotNull(T obj, String format, Object... args) {
checkCondition(obj != null, () -> new NullPointerException(String.format(format, args)));
}
// #endregion
// #region - checkCondition
public static <T extends Exception> void checkCondition(boolean condition, @Nonnull Supplier<T> e)
throws T {
if (!condition) {
throw e.get();
}
}
// #endregion
// #region - private constructor
private AssertTools() {
throw new IllegalStateException("Utility class");
}
// #endregion
}

View File

@@ -1,78 +1,39 @@
/*
* 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.math.BigDecimal;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
public class BigDecimals {
public static boolean equalsValue(@Nullable BigDecimal a, @Nullable BigDecimal b) {
public static final BigDecimal ZERO = new BigDecimal("0.00");
public static boolean equals(@Nullable BigDecimal a, @Nullable BigDecimal b) {
return (a == b) || (a != null && a.compareTo(b) == 0);
}
public static boolean gt(BigDecimal a, BigDecimal b) {
@Beta
public static boolean greaterThan(BigDecimal a, BigDecimal b) {
Preconditions.checkNotNull(a, "Parameter could not be null.");
Preconditions.checkNotNull(b, "Parameter could not be null.");
return (a != b) && (a.compareTo(b) > 0);
}
public static boolean ge(BigDecimal a, BigDecimal b) {
return gt(a, b) || equalsValue(a, b);
}
public static boolean lt(BigDecimal a, BigDecimal b) {
@Beta
public static boolean lessThan(BigDecimal a, BigDecimal b) {
Preconditions.checkNotNull(a, "Parameter could not be null.");
Preconditions.checkNotNull(b, "Parameter could not be null.");
return (a != b) && (a.compareTo(b) < 0);
}
public static boolean le(BigDecimal a, BigDecimal b) {
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)
@Beta
public static BigDecimal of(final String val) {
return (StringTools.isNotBlank(val)) ? new BigDecimal(val) : BigDecimal.ZERO;
return (StringUtils.isBlank(val)) ? ZERO : new BigDecimal(val);
}
private BigDecimals() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -21,38 +21,19 @@ 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
*
* <p>
* Java 8 {@link ConcurrentHashMap#computeIfAbsent(Object, Function)} 方法有 bug
* 可使用这个工具类的 {@link computeIfAbsentForJava8} 进行替换
*
* <p>
* <b>NOTE: 方法来自Dubboissues#2349</b>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0
* @see ConcurrentHashMap
* @see SafeConcurrentHashMap
*/
public class ConcurrentHashMapTools {
public class ConcurrentHashMapUtil { // TODO 添加文档注释
public static <K, V> V computeIfAbsent(
ConcurrentHashMap<K, V> map, final K key, // NOSONAR
public static <K, V> V computeIfAbsent(ConcurrentHashMap<K, V> map, final K key,
final Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(map, "map");
return JRE.isJava8()
? computeIfAbsentForJava8(map, key, mappingFunction)
: map.computeIfAbsent(key, mappingFunction);
}
public static <K, V> V computeIfAbsentForJava8(
ConcurrentHashMap<K, V> map, final K key, // NOSONAR
public static <K, V> V computeIfAbsentForJava8(ConcurrentHashMap<K, V> map, final K key,
final Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(key);
Objects.requireNonNull(mappingFunction);
V v = map.get(key);
if (null == v) {
@@ -68,7 +49,7 @@ public class ConcurrentHashMapTools {
return v;
}
private ConcurrentHashMapTools() {
private ConcurrentHashMapUtil() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -0,0 +1,19 @@
package xyz.zhouxy.plusone.commons.util;
/**
* 自定义结果
*
* @author zhouxy
*/
final class CustomResult extends UnifiedResponse {
CustomResult(Object status, String message) {
super(status, message);
}
CustomResult(Object status, String message, Object data) {
super(status, message, data);
}
private static final long serialVersionUID = -5794887914598566589L;
}

View File

@@ -1,722 +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.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
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 javax.annotation.Nonnull;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import xyz.zhouxy.plusone.commons.time.Quarter;
import xyz.zhouxy.plusone.commons.time.YearQuarter;
/**
* 日期时间工具类
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public class DateTimeTools {
/**
* 缓存时间格式化器
*/
private static final LoadingCache<String, DateTimeFormatter> DATE_TIME_FORMATTER_CACHE = CacheBuilder.newBuilder()
.maximumSize(20)
.build(new CacheLoader<String, DateTimeFormatter>() {
@Override
public DateTimeFormatter load(@Nonnull String pattern) throws Exception {
return DateTimeFormatter.ofPattern(pattern);
}
});
/**
* 获取时间格式化器
*
* @param pattern 时间格式
* @return 时间格式化器
*/
public static DateTimeFormatter getDateTimeFormatter(String pattern) {
return DATE_TIME_FORMATTER_CACHE.getUnchecked(pattern);
}
// #region - toString
/**
* 将日期时间转换为指定格式的字符串
*
* @param pattern 时间格式
* @param dateTime 日期时间
* @return 格式化的字符串
*/
public static String toString(String pattern, ZonedDateTime dateTime) {
return getDateTimeFormatter(pattern).format(dateTime);
}
/**
* 将时间戳转换为指定格式的字符串,使用系统默认时区
*
* @param pattern 时间格式
* @param instant 时间戳
* @return 格式化的字符串
*/
public static String toString(String pattern, Instant instant) {
ZonedDateTime dateTime = instant.atZone(ZoneId.systemDefault());
return toString(pattern, dateTime);
}
/**
* 将时间戳转换为指定格式的字符串,使用指定时区
*
* @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);
}
// #endregion
// #region - nowStr
/**
* 指定格式,返回当前时间戳对应的字符串
*
* @param pattern 时间格式
* @return 格式化的字符串
*/
public static String nowStr(String pattern) {
return toString(pattern, ZonedDateTime.now());
}
/**
* 指定格式,返回当前时间戳对应的字符串,使用指定时区
*
* @param pattern 时间格式
* @param zone 时区
* @return 格式化的字符串
*/
public static String nowStr(String pattern, ZoneId zone) {
return toString(pattern, Instant.now().atZone(zone));
}
// #endregion
// #region - toDate
/**
* 将时间戳转换为 {@link Date} 对象
*
* @param timeMillis 时间戳
* @return {@link Date} 对象
*/
public static Date toDate(long timeMillis) {
return Date.from(Instant.ofEpochMilli(timeMillis));
}
/**
* 将 {@link Calendar} 对象转换为 {@link Date} 对象
*
* @param calendar {@link Calendar} 对象
* @return {@link Date} 对象
*/
public static Date toDate(Calendar calendar) {
return calendar.getTime();
}
/**
* 将 {@link Instant} 对象转换为 {@link Date} 对象
*
* @param instant {@link Instant} 对象
* @return {@link Date} 对象
*/
public static Date toDate(Instant instant) {
return Date.from(instant);
}
/**
* 将 {@link ZonedDateTime} 对象转换为 {@link Date} 对象
*
* @param zonedDateTime {@link ZonedDateTime} 对象
* @return {@link Date} 对象
*/
public static Date toDate(ZonedDateTime zonedDateTime) {
return Date.from(zonedDateTime.toInstant());
}
/**
* 使用指定时区,将 {@link LocalDateTime} 对象转换为 {@link Date} 对象
*
* @param localDateTime {@link LocalDateTime} 对象
* @param zone 时区
* @return {@link Date} 对象
*/
public static Date toDate(LocalDateTime localDateTime, ZoneId zone) {
return Date.from(ZonedDateTime.of(localDateTime, zone).toInstant());
}
/**
* 使用指定时区,将 {@link LocalDate} 和 {@link LocalTime} 对象转换为 {@link Date} 对象
*
* @param localDate {@link LocalDate} 对象
* @param localTime {@link LocalTime} 对象
* @param zone 时区
* @return {@link Date} 对象
*/
public static Date toDate(LocalDate localDate, LocalTime localTime, ZoneId zone) {
return Date.from(ZonedDateTime.of(localDate, localTime, zone).toInstant());
}
// #endregion
// #region - toInstant
/**
* 将时间戳转换为 {@link Instant} 对象
*
* @param timeMillis 时间戳
* @return {@link Instant} 对象
*/
public static Instant toInstant(long timeMillis) {
return Instant.ofEpochMilli(timeMillis);
}
/**
* 将 {@link Date} 对象转换为 {@link Instant} 对象
*
* @param date {@link Date} 对象
* @return {@link Instant} 对象
*/
public static Instant toInstant(Date date) {
return date.toInstant();
}
/**
* 将 {@link Calendar} 对象转换为 {@link Instant} 对象
*
* @param calendar {@link Calendar} 对象
* @return {@link Instant} 对象
*/
public static Instant toInstant(Calendar calendar) {
return calendar.toInstant();
}
/**
* 将 {@link ZonedDateTime} 对象转换为 {@link Instant} 对象
*
* @param zonedDateTime {@link ZonedDateTime} 对象
* @return {@link Instant} 对象
* @deprecated 请使用 {@link ZonedDateTime#toInstant()} 方法
*/
@Deprecated
public static Instant toInstant(ZonedDateTime zonedDateTime) { // NOSONAR
return zonedDateTime.toInstant();
}
/**
* 使用指定时区,将 {@link LocalDateTime} 对象转换为 {@link Instant} 对象
*
* @param LocalDateTime {@link LocalDateTime} 对象
* @param zone 时区
* @return {@link Instant} 对象
*/
public static Instant toInstant(LocalDateTime localDateTime, ZoneId zone) {
return ZonedDateTime.of(localDateTime, zone).toInstant();
}
// #endregion
// #region - toZonedDateTime
/**
* 获取时间戳在指定时区的地区时间。
* <p>
* 传入不同 {@link ZoneId},获取到的 {@link ZonedDateTime} 对象实际上还是同一时间戳,
* 只是不同时区的表示。
* </p>
*
* @param timeMillis 时间戳
* @param zone 时区
* @return 带时区信息的地区时间
*/
public static ZonedDateTime toZonedDateTime(long timeMillis, ZoneId zone) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
* <p>
* 传入不同 {@link ZoneId},获取到的 {@link ZonedDateTime} 对象实际上还是同一时间戳,
* 只是不同时区的表示。
* </p>
*
* @param dateTime {@link Date} 对象
* @param zone 时区
* @return 带时区信息的地区时间
*/
public static ZonedDateTime toZonedDateTime(Date dateTime, ZoneId zone) {
return ZonedDateTime.ofInstant(dateTime.toInstant(), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
* <p>
* 传入不同 {@link ZoneId},获取到的 {@link ZonedDateTime} 对象实际上表示的还是还是同一时间戳的时间,
* 只是不同时区的表示。
* </p>
*
* @param dateTime {@link Date} 对象
* @param timeZone 时区
* @return 带时区信息的地区时间
*/
public static ZonedDateTime toZonedDateTime(Date dateTime, TimeZone timeZone) {
return ZonedDateTime.ofInstant(dateTime.toInstant(), timeZone.toZoneId());
}
/**
* 使用 {@code calendar} 对象的时区信息,将 {@link Calendar} 对象转换为 {@link ZonedDateTime}
* 对象。
*
* @param calendar{@link Calendar} 对象
* @return {@link ZonedDateTime} 对象
*/
public static ZonedDateTime toZonedDateTime(Calendar calendar) {
return calendar.toInstant().atZone(calendar.getTimeZone().toZoneId());
}
/**
* 使用指定的时区,将 {@link Calendar} 对象转换为 {@link ZonedDateTime} 对象。
*
* @param calendar {@link Calendar} 对象
* @param zone 时区
* @return {@link ZonedDateTime} 对象
*/
public static ZonedDateTime toZonedDateTime(Calendar calendar, ZoneId zone) {
return calendar.toInstant().atZone(zone);
}
/**
* 使用指定的时区,将 {@link Calendar} 对象转换为 {@link ZonedDateTime} 对象。
*
* @param calendar {@link Calendar} 对象
* @param zone 时区
* @return {@link ZonedDateTime} 对象
*/
public static ZonedDateTime toZonedDateTime(Calendar calendar, TimeZone zone) {
return calendar.toInstant().atZone(zone.toZoneId());
}
/**
* 创建带时区的地区时间
*
* @param localDateTime 地区时间
* @param zone 时区
* @return 带时区的地区时间
*
* @deprecated 使用 {@link ZonedDateTime#of(LocalDateTime, ZoneId)}
*/
@Deprecated
public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, ZoneId zone) { // NOSONAR
return ZonedDateTime.of(localDateTime, zone);
}
// #endregion
// #region - toLocalDateTime
/**
* 获取时间戳在指定时区的地区时间。
*
* @param timeMillis 时间戳
* @param zone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zone) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
*
* @param dateTime {@link Date} 对象
* @param zone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(Date dateTime, ZoneId zone) {
return LocalDateTime.ofInstant(dateTime.toInstant(), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
*
* @param dateTime {@link Date} 对象
* @param timeZone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(Date dateTime, TimeZone timeZone) {
return LocalDateTime.ofInstant(dateTime.toInstant(), timeZone.toZoneId());
}
/**
* 获取 {@link Calendar} 所表示的时间戳,在指定时区的地区时间。
*
* @param calendar {@link Calendar} 对象
* @param zone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(Calendar calendar, ZoneId zone) {
return LocalDateTime.ofInstant(calendar.toInstant(), zone);
}
/**
* 获取 {@link Calendar} 所表示的时间戳,在指定时区的地区时间。
*
* @param calendar {@link Calendar} 对象
* @param zone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(Calendar calendar, TimeZone zone) {
return LocalDateTime.ofInstant(calendar.toInstant(), zone.toZoneId());
}
/**
* 获取 {@link ZonedDateTime} 所表示的时间戳,在指定时区的地区时间。
*
* @param zonedDateTime {@link ZonedDateTime} 对象
* @param zone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(ZonedDateTime zonedDateTime, ZoneId zone) {
return LocalDateTime.ofInstant(zonedDateTime.toInstant(), zone);
}
// #endregion
// ====================
// #region - toJodaInstant
/**
* 将 {@link java.time.Instant} 转换为 {@link org.joda.time.Instant}
*
* @param instant {@link java.time.Instant} 对象
* @return {@link org.joda.time.Instant} 对象
*/
public static org.joda.time.Instant toJodaInstant(java.time.Instant instant) {
return new org.joda.time.Instant(instant.toEpochMilli());
}
/**
* 将 {@link java.time.ZonedDateTime} 转换为 {@link org.joda.time.Instant}
*
* @param zonedDateTime {@link java.time.ZonedDateTime} 对象
* @return {@link org.joda.time.Instant} 对象
*/
public static org.joda.time.Instant toJodaInstant(java.time.ZonedDateTime zonedDateTime) {
return toJodaInstant(zonedDateTime.toInstant());
}
/**
* 计算指定时区的地区时间,对应的时间戳。结果为 {@link org.joda.time.Instant} 对象
*
* @param localDateTime {@link java.time.LocalDateTime} 对象
* @param zone 时区
* @return {@link org.joda.time.Instant} 对象
*/
public static org.joda.time.Instant toJodaInstant(java.time.LocalDateTime localDateTime, java.time.ZoneId zone) {
return toJodaInstant(java.time.ZonedDateTime.of(localDateTime, zone));
}
// #endregion
// #region - toJavaInstant
/**
* 将 {@link org.joda.time.Instant} 对象转换为 {@link java.time.Instant} 对象
*
* @param instant {@link org.joda.time.Instant} 对象
* @return {@link java.time.Instant} 对象
*/
public static java.time.Instant toJavaInstant(org.joda.time.Instant instant) {
return toInstant(instant.getMillis());
}
/**
* 将 joda-time 中的 {@link org.joda.time.DateTime} 对象转换为 Java 的
* {@link java.time.Instant} 对象
*
* @param dateTime joda-time 中表示日期时间的 {@link org.joda.time.DateTime} 对象
* @return Java 表示时间戳的 {@link java.time.Instant} 对象
*/
public static java.time.Instant toJavaInstant(org.joda.time.DateTime dateTime) {
return toInstant(dateTime.getMillis());
}
/**
* 将 joda-time 中的 {@link org.joda.time.LocalDateTime} 对象和
* {@link org.joda.time.DateTimeZone} 对象
* 转换为 Java 中的 {@link java.time.Instant} 对象
*
* @param localDateTime
* @param zone
* @return
*/
public static java.time.Instant toJavaInstant(
org.joda.time.LocalDateTime localDateTime,
org.joda.time.DateTimeZone zone) {
return toJavaInstant(localDateTime.toDateTime(zone));
}
// #endregion
// #region - toJodaDateTime
/**
* 将 Java 中表示日期时间的 {@link java.time.ZonedDateTime} 对象
* 转换为 joda-time 的 {@link org.joda.time.DateTime} 对象
*
* @param zonedDateTime 日期时间
* @return joda-time 中对应的 {@link org.joda.time.DateTime} 对象
*/
public static org.joda.time.DateTime toJodaDateTime(java.time.ZonedDateTime zonedDateTime) {
org.joda.time.DateTimeZone zone = org.joda.time.DateTimeZone.forID(zonedDateTime.getZone().getId());
return toJodaInstant(zonedDateTime.toInstant()).toDateTime(zone);
}
/**
* 将 java.time 中表示日期时间的 {@link java.time.LocalDateTime} 对象和表示时区的
* {@link java.time.ZoneId} 对象转换为 joda-time 中对应的 {@link org.joda.time.DateTime}
* 对象
* 转换为 joda-time 中对应的 {@link org.joda.time.DateTime} 对象
*
* @param localDateTime 日期时间
* @param zone 时区
* @return joda-time 中对应的 {@link org.joda.time.DateTime} 对象
*/
public static org.joda.time.DateTime toJodaDateTime(
java.time.LocalDateTime localDateTime,
java.time.ZoneId zone) {
org.joda.time.DateTimeZone dateTimeZone = toJodaZone(zone);
return toJodaInstant(ZonedDateTime.of(localDateTime, zone).toInstant()).toDateTime(dateTimeZone);
}
/**
* 计算时间戳在指定时区对应的时间,结果使用 {@link org.joda.time.DateTime} 表示
*
* @param instant java.time 中的时间戳
* @param zone java.time 中的时区
* @return joda-time 中带时区的日期时间
*/
public static org.joda.time.DateTime toJodaDateTime(
java.time.Instant instant,
java.time.ZoneId zone) {
org.joda.time.DateTimeZone dateTimeZone = toJodaZone(zone);
return toJodaInstant(instant).toDateTime(dateTimeZone);
}
// #endregion
// #region - toZonedDateTime
/**
* 将 joda-time 中带时区的日期时间,转换为 java.time 中带时区的日期时间
*
* @param dateTime joda-time 中带时区的日期时间
* @return java.time 中带时区的日期时间
*/
public static java.time.ZonedDateTime toZonedDateTime(org.joda.time.DateTime dateTime) {
java.time.ZoneId zone = dateTime.getZone().toTimeZone().toZoneId();
return toJavaInstant(dateTime.toInstant()).atZone(zone);
}
/**
* 将 joda-time 中的 {@link org.joda.time.LocalDateTime} 和
* {@link org.joda.time.DateTimeZone}
* 转换为 java.time 中的 {@link java.time.ZonedDateTime}
*
* @param localDateTime joda-time 中的地区时间
* @param dateTimeZone joda-time 中的时区
* @return java.time 中带时区的日期时间
*/
public static java.time.ZonedDateTime toZonedDateTime(
org.joda.time.LocalDateTime localDateTime,
org.joda.time.DateTimeZone dateTimeZone) {
java.time.ZoneId zone = toJavaZone(dateTimeZone);
return toJavaInstant(localDateTime, dateTimeZone).atZone(zone);
}
/**
* 获取 joda-time 中的 {@link org.joda.time.Instant} 在指定时区的时间,用 Java 8
* {@link java.time.ZonedDateTime} 表示
*
* @param instant joda-time 中的时间戳
* @param dateTimeZone joda-time 中的时区
* @return
*/
public static java.time.ZonedDateTime toZonedDateTime(
org.joda.time.Instant instant,
org.joda.time.DateTimeZone dateTimeZone) {
java.time.ZoneId zone = toJavaZone(dateTimeZone);
return toJavaInstant(instant).atZone(zone);
}
// #endregion
// #region - toJodaLocalDateTime
/**
* 将 {@link java.time.LocalDateTime} 转换为 {@link org.joda.time.LocalDateTime}
*
* @param localDateTime Java 8 LocalDateTime
* @return joda-time LocalDateTime
*/
public static org.joda.time.LocalDateTime toJodaLocalDateTime(java.time.LocalDateTime localDateTime) {
java.time.ZoneId javaZone = java.time.ZoneId.systemDefault();
org.joda.time.DateTimeZone jodaZone = toJodaZone(javaZone);
return toJodaInstant(localDateTime, javaZone).toDateTime(jodaZone).toLocalDateTime();
}
// #endregion
// #region - toJavaLocalDateTime
/**
* 将 {@link org.joda.time.LocalDateTime} 转换为 {@link java.time.LocalDateTime}
*
* @param localDateTime joda-time LocalDateTime
* @return Java 8 LocalDateTime
*/
public static java.time.LocalDateTime toJavaLocalDateTime(org.joda.time.LocalDateTime localDateTime) {
org.joda.time.DateTimeZone jodaZone = org.joda.time.DateTimeZone.getDefault();
java.time.ZoneId javaZone = toJavaZone(jodaZone);
return toJavaInstant(localDateTime, jodaZone).atZone(javaZone).toLocalDateTime();
}
// #endregion
// #region - ZoneId <--> DateTimeZone
/**
* 转换 Java API 和 joda-time API 表示时区的对象
*
* @param jodaZone joda-time API 中表示时区的对象
* @return Java API 中表示时区的对象
*/
public static java.time.ZoneId toJavaZone(org.joda.time.DateTimeZone jodaZone) {
return jodaZone.toTimeZone().toZoneId();
}
/**
* 转换 Java API 和 joda-time API 表示时区的对象
*
* @param zone Java API 中表示时区的对象
* @return joda-time API 中表示时区的对象
*/
public static org.joda.time.DateTimeZone toJodaZone(java.time.ZoneId zone) {
return org.joda.time.DateTimeZone.forID(zone.getId());
}
// #endregion
// #region - YearQuarter & Quarter
/**
* 获取指定日期所在季度
*
* @param date 日期
* @return 日期所在的季度
*/
public static YearQuarter getQuarter(Date date) {
return YearQuarter.of(date);
}
/**
* 获取指定日期所在季度
*
* @param date 日期
* @return 日期所在的季度
*/
public static YearQuarter getQuarter(Calendar date) {
return YearQuarter.of(date);
}
/**
* 获取指定月份所在季度
*
* @param month 月份
* @return 季度
*/
public static Quarter getQuarter(Month month) {
return Quarter.fromMonth(month);
}
/**
* 获取指定年月所在季度
*
* @param year 年
* @param month 月
* @return 季度
*/
public static YearQuarter getQuarter(int year, Month month) {
return YearQuarter.of(YearMonth.of(year, month));
}
/**
* 获取指定年月所在季度
*
* @param yearMonth 年月
* @return 季度
*/
public static YearQuarter getQuarter(YearMonth yearMonth) {
return YearQuarter.of(yearMonth);
}
/**
* 获取指定日期所在季度
*
* @param date 日期
* @return 日期所在的季度
*/
public static YearQuarter getQuarter(LocalDate date) {
return YearQuarter.of(date);
}
// #endregion
/**
* 私有构造方法,明确标识该常量类的作用。
*/
private DateTimeTools() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -0,0 +1,331 @@
package xyz.zhouxy.plusone.commons.util;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
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.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone;
import com.google.common.base.Preconditions;
import xyz.zhouxy.plusone.commons.collection.SafeConcurrentHashMap;
import xyz.zhouxy.plusone.commons.collection.MapWrapper;
public class DateTimeUtil {
private static final MapWrapper<String, DateTimeFormatter> DATE_TIME_FORMATTER_CACHE = MapWrapper
.<String, DateTimeFormatter>wrap(new SafeConcurrentHashMap<>())
.keyChecker(pattern -> Preconditions.checkArgument(StringUtils.isNotBlank(pattern), "The pattern could not be blank."))
.valueChecker(formatter -> Preconditions.checkNotNull(formatter, "The formatter could not be null."))
.build();
public static DateTimeFormatter getDateTimeFormatter(String pattern) {
return DATE_TIME_FORMATTER_CACHE.computeIfAbsent(pattern, DateTimeFormatter::ofPattern);
}
public static String toString(String pattern, ZonedDateTime dateTime) {
return getDateTimeFormatter(pattern).format(dateTime);
}
public static String toString(String pattern, Instant instant) {
ZonedDateTime dateTime = instant.atZone(ZoneId.systemDefault());
return toString(pattern, dateTime);
}
public static String toString(String pattern, Instant instant, ZoneId zone) {
ZonedDateTime dateTime = instant.atZone(zone);
return toString(pattern, dateTime);
}
public static String nowStr(String pattern) {
return toString(pattern, ZonedDateTime.now());
}
public static String nowStr(String pattern, ZoneId zone) {
return toString(pattern, Instant.now().atZone(zone));
}
// toDate
public static Date toDate(long timeMillis) {
return Date.from(Instant.ofEpochMilli(timeMillis));
}
public static Date toDate(Calendar calendar) {
return calendar.getTime();
}
public static Date toDate(Instant instant) {
return Date.from(instant);
}
public static Date toDate(ZonedDateTime zonedDateTime) {
return Date.from(zonedDateTime.toInstant());
}
public static Date toDate(LocalDateTime localDateTime, ZoneId zone) {
return Date.from(ZonedDateTime.of(localDateTime, zone).toInstant());
}
public static Date toDate(LocalDate localDate, LocalTime localTime, ZoneId zone) {
return Date.from(ZonedDateTime.of(localDate, localTime, zone).toInstant());
}
// toInstant
public static Instant toInstant(long timeMillis) {
return Instant.ofEpochMilli(timeMillis);
}
public static Instant toInstant(Date date) {
return date.toInstant();
}
public static Instant toInstant(Calendar calendar) {
return calendar.toInstant();
}
/**
* 获取 {@link Instant} 对象
*
* @param zonedDateTime 带时区信息的地区时间
* @return 时间点
*
* @deprecated 使用 {@link ZonedDateTime#toInstant()}
*/
@Deprecated
public static Instant toInstant(ZonedDateTime zonedDateTime) {
return zonedDateTime.toInstant();
}
public static Instant toInstant(LocalDateTime localDateTime, ZoneId zone) {
return ZonedDateTime.of(localDateTime, zone).toInstant();
}
// toZonedDateTime
/**
* 获取时间戳在指定时区的地区时间。
* <p>
* 传入不同 {@link ZoneId},获取到的 {@link ZonedDateTime} 对象实际上还是同一时间戳,
* 只是不同时区的表示。
* </p>
*
* @param timeMillis 时间戳
* @param zone 时区
* @return 带时区信息的地区时间
*/
public static ZonedDateTime toZonedDateTime(long timeMillis, ZoneId zone) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
* <p>
* 传入不同 {@link ZoneId},获取到的 {@link ZonedDateTime} 对象实际上还是同一时间戳,
* 只是不同时区的表示。
* </p>
*
* @param dateTime {@link Date} 对象
* @param zone 时区
* @return 带时区信息的地区时间
*/
public static ZonedDateTime toZonedDateTime(Date dateTime, ZoneId zone) {
return ZonedDateTime.ofInstant(dateTime.toInstant(), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
* <p>
* 传入不同 {@link ZoneId},获取到的 {@link ZonedDateTime} 对象实际上表示的还是还是同一时间戳的时间,
* 只是不同时区的表示。
* </p>
*
* @param dateTime {@link Date} 对象
* @param timeZone 时区
* @return 带时区信息的地区时间
*/
public static ZonedDateTime toZonedDateTime(Date dateTime, TimeZone timeZone) {
return ZonedDateTime.ofInstant(dateTime.toInstant(), timeZone.toZoneId());
}
public static ZonedDateTime toZonedDateTime(Calendar calendar, ZoneId zone) {
return calendar.toInstant().atZone(zone);
}
public static ZonedDateTime toZonedDateTime(Calendar calendar, TimeZone zone) {
return calendar.toInstant().atZone(zone.toZoneId());
}
/**
* 创建带时区的地区时间
*
* @param localDateTime 地区时间
* @param zone 时区
* @return 带时区的地区时间
*
* @deprecated 使用 {@link ZonedDateTime#of(LocalDateTime, ZoneId)}
*/
@Deprecated
public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, ZoneId zone) {
return ZonedDateTime.of(localDateTime, zone);
}
// toLocalDateTime
/**
* 获取时间戳在指定时区的地区时间。
*
* @param timeMillis 时间戳
* @param zone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zone) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
*
* @param dateTime {@link Date} 对象
* @param zone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(Date dateTime, ZoneId zone) {
return LocalDateTime.ofInstant(dateTime.toInstant(), zone);
}
/**
* 获取 {@link Date} 所表示的时间戳,在指定时区的地区时间。
*
* @param dateTime {@link Date} 对象
* @param timeZone 时区
* @return 地区时间
*/
public static LocalDateTime toLocalDateTime(Date dateTime, TimeZone timeZone) {
return LocalDateTime.ofInstant(dateTime.toInstant(), timeZone.toZoneId());
}
public static LocalDateTime toLocalDateTime(Calendar calendar, ZoneId zone) {
return LocalDateTime.ofInstant(calendar.toInstant(), zone);
}
public static LocalDateTime toLocalDateTime(Calendar calendar, TimeZone zone) {
return LocalDateTime.ofInstant(calendar.toInstant(), zone.toZoneId());
}
public static LocalDateTime toLocalDateTime(ZonedDateTime zonedDateTime, ZoneId zone) {
return LocalDateTime.ofInstant(zonedDateTime.toInstant(), zone);
}
// ====================
// toJodaInstant
public static org.joda.time.Instant toJodaInstant(java.time.Instant instant) {
return new org.joda.time.Instant(instant.toEpochMilli());
}
public static org.joda.time.Instant toJodaInstant(java.time.ZonedDateTime zonedDateTime) {
return toJodaInstant(zonedDateTime.toInstant());
}
public static org.joda.time.Instant toJodaInstant(java.time.LocalDateTime localDateTime, java.time.ZoneId zone) {
return toJodaInstant(java.time.ZonedDateTime.of(localDateTime, zone));
}
// toJavaInstant
public static java.time.Instant toJavaInstant(org.joda.time.Instant instant) {
return toInstant(instant.getMillis());
}
public static java.time.Instant toJavaInstant(org.joda.time.DateTime dateTime) {
return toInstant(dateTime.getMillis());
}
public static java.time.Instant toJavaInstant(
org.joda.time.LocalDateTime localDateTime,
org.joda.time.DateTimeZone zone) {
return toJavaInstant(localDateTime.toDateTime(zone));
}
// toJodaDateTime
public static org.joda.time.DateTime toJodaDateTime(java.time.ZonedDateTime zonedDateTime) {
org.joda.time.DateTimeZone zone = org.joda.time.DateTimeZone.forID(zonedDateTime.getZone().getId());
return toJodaInstant(zonedDateTime.toInstant()).toDateTime(zone);
}
public static org.joda.time.DateTime toJodaDateTime(
java.time.LocalDateTime localDateTime,
java.time.ZoneId zone) {
org.joda.time.DateTimeZone dateTimeZone = toJodaTime(zone);
return toJodaInstant(ZonedDateTime.of(localDateTime, zone).toInstant()).toDateTime(dateTimeZone);
}
public static org.joda.time.DateTime toJodaDateTime(
java.time.Instant instant,
java.time.ZoneId zone) {
org.joda.time.DateTimeZone dateTimeZone = toJodaTime(zone);
return toJodaInstant(instant).toDateTime(dateTimeZone);
}
// toZonedDateTime
public static java.time.ZonedDateTime toZonedDateTime(org.joda.time.DateTime dateTime) {
java.time.ZoneId zone = dateTime.getZone().toTimeZone().toZoneId();
return toJavaInstant(dateTime.toInstant()).atZone(zone);
}
public static java.time.ZonedDateTime toZonedDateTime(
org.joda.time.LocalDateTime localDateTime,
org.joda.time.DateTimeZone dateTimeZone) {
java.time.ZoneId zone = toJavaZone(dateTimeZone);
return toJavaInstant(localDateTime, dateTimeZone).atZone(zone);
}
public static java.time.ZonedDateTime toZonedDateTime(
org.joda.time.Instant instant,
org.joda.time.DateTimeZone dateTimeZone) {
java.time.ZoneId zone = toJavaZone(dateTimeZone);
return toJavaInstant(instant).atZone(zone);
}
// toJodaLocalDateTime
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);
return toJodaInstant(localDateTime, javaZone).toDateTime(jodaZone).toLocalDateTime();
}
// toJavaLocalDateTime
public static java.time.LocalDateTime toJavaLocalDateTime(org.joda.time.LocalDateTime localDateTime) {
org.joda.time.DateTimeZone jodaZone = org.joda.time.DateTimeZone.getDefault();
java.time.ZoneId javaZone = toJavaZone(jodaZone);
return toJavaInstant(localDateTime, jodaZone).atZone(javaZone).toLocalDateTime();
}
public static java.time.ZoneId toJavaZone(org.joda.time.DateTimeZone jodaZone) {
return jodaZone.toTimeZone().toZoneId();
}
public static DateTimeZone toJodaTime(java.time.ZoneId zone) {
return org.joda.time.DateTimeZone.forID(zone.getId());
}
private DateTimeUtil() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-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.
@@ -25,11 +25,11 @@ import com.google.common.base.Preconditions;
/**
* 枚举工具类
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public final class EnumTools {
public final class EnumUtil {
private EnumTools() {
private EnumUtil() {
throw new IllegalStateException("Utility class");
}
@@ -43,10 +43,10 @@ public final class EnumTools {
* @deprecated 不推荐使用枚举的 ordinal
*/
@Deprecated
public static <E extends Enum<?>> E valueOf(Class<E> clazz, int ordinal) { // NOSONAR 该方法弃用但不删掉
public static <E extends Enum<?>> E valueOf(Class<E> clazz, int ordinal) {
Preconditions.checkNotNull(clazz, "Clazz must not be null.");
E[] values = clazz.getEnumConstants();
AssertTools.checkCondition((ordinal >= 0 && ordinal < values.length),
PreconditionsExt.check((ordinal >= 0 && ordinal < values.length),
() -> new EnumConstantNotPresentException(clazz, Integer.toString(ordinal)));
return values[ordinal];
}
@@ -62,7 +62,7 @@ public final class EnumTools {
* @deprecated 不推荐使用枚举的 ordinal
*/
@Deprecated
public static <E extends Enum<?>> E valueOf(Class<E> clazz, @Nullable Integer ordinal, E defaultValue) { // NOSONAR 该方法弃用但不删掉
public static <E extends Enum<?>> E valueOf(Class<E> clazz, @Nullable Integer ordinal, E defaultValue) {
if (null == ordinal) {
return defaultValue;
}
@@ -80,7 +80,7 @@ public final class EnumTools {
* @deprecated 不推荐使用枚举的 ordinal
*/
@Deprecated
public static <E extends Enum<?>> E getValueOrDefault( // NOSONAR 该方法弃用但不删掉
public static <E extends Enum<?>> E getValueOrDefault(
Class<E> clazz,
@Nullable Integer ordinal,
Supplier<E> defaultValue) {
@@ -100,7 +100,7 @@ public final class EnumTools {
* @deprecated 不推荐使用枚举的 ordinal
*/
@Deprecated
public static <E extends Enum<?>> E getValueOrDefault(Class<E> clazz, @Nullable Integer ordinal) { // NOSONAR 该方法弃用但不删掉
public static <E extends Enum<?>> E getValueOrDefault(Class<E> clazz, @Nullable Integer ordinal) {
return getValueOrDefault(clazz, ordinal, () -> {
Preconditions.checkNotNull(clazz, "Clazz must not be null.");
E[] values = clazz.getEnumConstants();
@@ -118,7 +118,7 @@ public final class EnumTools {
* @deprecated 不推荐使用枚举的 ordinal
*/
@Deprecated
public static <E extends Enum<?>> E getValueNullable(Class<E> clazz, @Nullable Integer ordinal) { // NOSONAR 该方法弃用但不删掉
public static <E extends Enum<?>> E getValueNullable(Class<E> clazz, @Nullable Integer ordinal) {
return valueOf(clazz, ordinal, null);
}
@@ -134,7 +134,7 @@ public final class EnumTools {
/**
* 校验枚举的 ordinal
*
*
* @param <E> 枚举类型
* @param clazz 枚举类型
* @param ordinal The ordinal
@@ -147,7 +147,7 @@ public final class EnumTools {
/**
* 校验枚举的 ordinal如果 ordinal {@code null}则返回 {@code 0}
*
*
* @param <E> 枚举类型
* @param clazz 枚举类型
* @param ordinal The ordinal
@@ -160,7 +160,7 @@ public final class EnumTools {
/**
* 校验枚举的 ordinal如果 ordinal {@code null}则返回 {@code defaultValue}
*
*
* @param <E> 枚举类型
* @param clazz 枚举类型
* @param ordinal The ordinal

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-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.
@@ -16,13 +16,13 @@
package xyz.zhouxy.plusone.commons.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Preconditions;
@@ -30,20 +30,13 @@ import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
/**
* 枚举类
*
* 参考 <a href="https://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/">Enumeration classes</a>
*
* @deprecated 设计 Enumeration 的灵感来自于 .net 社区,因为 C# 的枚举不带行为。
* 但 Java 的枚举可以带行为,故大多数情况下不需要这种设计。
*/
@Deprecated
public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移除
implements Comparable<T> {
public abstract class Enumeration<T extends Enumeration<T>> implements Comparable<T> {
protected final int id;
protected final String name;
protected Enumeration(final int id, final String name) {
Preconditions.checkArgument(StringTools.isNotBlank(name), "Name of enumeration must has text.");
Preconditions.checkArgument(StringUtils.isNotBlank(name), "Name of enumeration must has text.");
this.id = id;
this.name = name;
}
@@ -90,15 +83,18 @@ public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移
this.valueMap = valueMap;
}
@SafeVarargs
@StaticFactoryMethod(ValueSet.class)
public static <T extends Enumeration<T>> ValueSet<T> of(T[] values) {
Map<Integer, T> temp = Arrays.stream(values)
.collect(Collectors.toMap(Enumeration::getId, Function.identity()));
public static <T extends Enumeration<T>> ValueSet<T> of(T... values) {
Map<Integer, T> temp = new HashMap<>();
for (T value : values) {
temp.put(value.getId(), value);
}
return new ValueSet<>(Collections.unmodifiableMap(temp));
}
public T get(int id) {
Preconditions.checkArgument(this.valueMap.containsKey(id), "[%s] 对应的值不存在", id);
Preconditions.checkArgument(this.valueMap.containsKey(id), "%s 对应的值不存在", id);
return this.valueMap.get(id);
}

View File

@@ -0,0 +1,40 @@
package xyz.zhouxy.plusone.commons.util;
import com.google.common.base.Strings;
import xyz.zhouxy.plusone.commons.base.IWithCode;
/**
* 错误结果
*
* @author zhouxy
*/
final class ErrorResult extends UnifiedResponse {
private static final String DEFAULT_ERR_STATUS = "9999999";
ErrorResult(String message) {
super(DEFAULT_ERR_STATUS, message);
}
ErrorResult(String message, Object data) {
super(DEFAULT_ERR_STATUS, message, data);
}
ErrorResult(Object status, String message) {
super(status, message);
}
ErrorResult(Object status, String message, Object data) {
super(status, message, data);
}
ErrorResult(Object status, Throwable e) {
super(status, Strings.nullToEmpty(e.getMessage()));
}
<E extends Throwable & IWithCode<?>> ErrorResult(E e) {
super(e.getCode(), Strings.nullToEmpty(e.getMessage()));
}
private static final long serialVersionUID = -1680792957826923092L;
}

View File

@@ -1,19 +1,3 @@
/*
* 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.Map;
@@ -40,15 +24,15 @@ public class IdGenerator {
}
public static String toSimpleString(UUID uuid) {
return (uuidDigits(uuid.getMostSignificantBits() >> 32, 8) +
uuidDigits(uuid.getMostSignificantBits() >> 16, 4) +
uuidDigits(uuid.getMostSignificantBits(), 4) +
uuidDigits(uuid.getLeastSignificantBits() >> 48, 4) +
uuidDigits(uuid.getLeastSignificantBits(), 12));
return (digits(uuid.getMostSignificantBits() >> 32, 8) +
digits(uuid.getMostSignificantBits() >> 16, 4) +
digits(uuid.getMostSignificantBits(), 4) +
digits(uuid.getLeastSignificantBits() >> 48, 4) +
digits(uuid.getLeastSignificantBits(), 12));
}
/** Returns val represented by the specified number of hex digits. */
private static String uuidDigits(long val, int digits) {
private static String digits(long val, int digits) {
long hi = 1L << (digits * 4);
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
}

View File

@@ -1,30 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
* Copyright 1999-2019 Seata.io Group.
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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
*
* 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.
* http://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.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;
import xyz.zhouxy.plusone.commons.exception.system.NoAvailableMacFoundException;
/**
* IdWorker from Seata.
*
* @author funkye
* @author selfishlover
*/
public class IdWorker {
/**
@@ -76,9 +74,8 @@ public class IdWorker {
/**
* instantiate an IdWorker using given workerId
* @param workerId if null, then will auto assign one
*/
public IdWorker(Long workerId) {
public IdWorker(long workerId) {
initTimestampAndSequence();
initWorkerId(workerId);
}
@@ -96,10 +93,7 @@ public class IdWorker {
* init workerId
* @param workerId if null, then auto generate one
*/
private void initWorkerId(Long workerId) {
if (workerId == null) {
workerId = generateWorkerId();
}
private void initWorkerId(long workerId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
String message = String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID);
throw new IllegalArgumentException(message);
@@ -133,7 +127,8 @@ public class IdWorker {
if (current >= newest) {
try {
Thread.sleep(5);
} catch (InterruptedException ignore) { // NOSONAR don't care
} catch (InterruptedException ignore) {
// don't care
}
}
}
@@ -144,45 +139,4 @@ public class IdWorker {
private long getNewestTimestamp() {
return System.currentTimeMillis() - TWEPOCH;
}
/**
* auto generate workerId, try using mac first, if failed, then randomly generate one
* @return workerId
*/
private static long generateWorkerId() {
try {
return generateWorkerIdBaseOnMac();
} catch (Exception e) {
return generateRandomWorkerId();
}
}
/**
* use lowest 10 bit of available MAC as workerId
* @return workerId
* @throws SocketException
* @throws NoAvailableMacFoundException when there is no available mac found
*/
private static long generateWorkerIdBaseOnMac() throws SocketException, NoAvailableMacFoundException {
Enumeration<NetworkInterface> all = NetworkInterface.getNetworkInterfaces();
while (all.hasMoreElements()) {
NetworkInterface networkInterface = all.nextElement();
boolean isLoopback = networkInterface.isLoopback();
boolean isVirtual = networkInterface.isVirtual();
byte[] mac = networkInterface.getHardwareAddress();
if (isLoopback || isVirtual || mac == null) {
continue;
}
return ((mac[4] & 0B11) << 8) | (mac[5] & 0xFF);
}
throw new NoAvailableMacFoundException();
}
/**
* randomly generate one as workerId
* @return workerId
*/
private static long generateRandomWorkerId() {
return ThreadLocalRandom.current().nextInt(MAX_WORKER_ID + 1);
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
@Beta
public class MoreArrays {
public static float[] concatFloatArray(Collection<float[]> arrays) {
int length = 0;
for (float[] arr : arrays) {
length += arr.length;
}
float[] result = new float[length];
int i = 0;
for (float[] arr : arrays) {
System.arraycopy(arr, 0, result, i, arr.length);
i = arr.length;
}
return result;
}
public static double[] concatDoubleArray(Collection<double[]> arrays) {
int length = 0;
for (double[] arr : arrays) {
length += arr.length;
}
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;
}
public static byte[] concatByteArray(Collection<byte[]> arrays) {
int length = 0;
for (byte[] arr : arrays) {
length += arr.length;
}
byte[] result = new byte[length];
int i = 0;
for (byte[] arr : arrays) {
System.arraycopy(arr, 0, result, i, arr.length);
i = arr.length;
}
return result;
}
public static long[] concatLongArray(Collection<long[]> arrays) {
int length = 0;
for (long[] arr : arrays) {
length += arr.length;
}
long[] result = new long[length];
int i = 0;
for (long[] arr : arrays) {
System.arraycopy(arr, 0, result, i, arr.length);
i = arr.length;
}
return result;
}
public static int[] concatIntArray(Collection<int[]> arrays) {
int length = 0;
for (int[] arr : arrays) {
length += arr.length;
}
int[] result = new int[length];
int i = 0;
for (int[] arr : arrays) {
System.arraycopy(arr, 0, result, i, arr.length);
i = arr.length;
}
return result;
}
public static <T> List<T> concatToList(@Nullable Collection<T[]> arrays) {
if (arrays == null || arrays.isEmpty()) {
return Collections.emptyList();
}
int length = 0;
for (T[] arr : arrays) {
length += arr.length;
}
final List<T> result = new ArrayList<>(length);
for (T[] arr : arrays) {
Collections.addAll(result, arr);
}
return result;
}
private MoreArrays() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -0,0 +1,169 @@
package xyz.zhouxy.plusone.commons.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.google.common.collect.Table;
import xyz.zhouxy.plusone.commons.collection.SafeConcurrentHashMap;
import xyz.zhouxy.plusone.commons.collection.SynchronizedTable;
public class MoreCollections {
// isEmpty
public static boolean isEmpty(@Nullable Collection<?> collection) {
return collection == null || collection.isEmpty();
}
public static boolean isEmpty(@Nullable Map<?, ?> map) {
return map == null || map.isEmpty();
}
// isNotEmpty
public static boolean isNotEmpty(@Nullable Collection<?> collection) {
return collection != null && !collection.isEmpty();
}
public static boolean isNotEmpty(@Nullable Map<?, ?> map) {
return map != null && !map.isEmpty();
}
// Collection -> Map
public static <K, V> HashMap<K, V> toHashMap(
Iterable<V> c,
Function<? super V, K> keyGenerator,
int initialCapacity) {
HashMap<K, V> map = new HashMap<>(initialCapacity);
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> HashMap<K, V> toHashMap(
Collection<V> c,
Function<? super V, K> keyGenerator) {
return toHashMap(c, keyGenerator, c.size());
}
public static <K, V> SafeConcurrentHashMap<K, V> toConcurrentHashMap(
Iterable<V> c,
Function<? super V, K> keyGenerator,
int initialCapacity) {
SafeConcurrentHashMap<K, V> map = new SafeConcurrentHashMap<>(initialCapacity);
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> SafeConcurrentHashMap<K, V> toConcurrentHashMap(
Collection<V> c,
Function<? super V, K> keyGenerator) {
return toConcurrentHashMap(c, keyGenerator, c.size());
}
public static <K extends Comparable<? super K>, V> TreeMap<K, V> toTreeMap(
Iterable<V> c,
Function<? super V, K> keyGenerator) {
TreeMap<K, V> map = new TreeMap<>();
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> TreeMap<K, V> toTreeMap(
Iterable<V> c,
Function<? super V, K> keyGenerator,
Comparator<? super K> keycComparator) {
TreeMap<K, V> map = new TreeMap<>(keycComparator);
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> void fillIntoEmptyMap(
Map<K, ? super V> map,
Iterable<V> c,
Function<? super V, K> keyGenerator) {
Preconditions.checkNotNull(map);
Preconditions.checkNotNull(c);
Preconditions.checkNotNull(keyGenerator);
Preconditions.checkArgument(map.isEmpty(), "The map should be empty.");
for (V v : c) {
map.put(keyGenerator.apply(v), v);
}
}
// array -> map
public static <K, V> HashMap<K, V> toHashMap(
V[] c,
Function<? super V, K> keyGenerator,
int initialCapacity) {
HashMap<K, V> map = new HashMap<>(initialCapacity);
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> HashMap<K, V> toHashMap(
V[] c,
Function<? super V, K> keyGenerator) {
return toHashMap(c, keyGenerator, c.length);
}
public static <K, V> SafeConcurrentHashMap<K, V> toConcurrentHashMap(
V[] c,
Function<? super V, K> keyGenerator,
int initialCapacity) {
SafeConcurrentHashMap<K, V> map = new SafeConcurrentHashMap<>(initialCapacity);
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> SafeConcurrentHashMap<K, V> toConcurrentHashMap(
V[] c,
Function<? super V, K> keyGenerator) {
return toConcurrentHashMap(c, keyGenerator, c.length);
}
public static <K extends Comparable<? super K>, V> TreeMap<K, V> toTreeMap(
V[] c,
Function<? super V, K> keyGenerator) {
TreeMap<K, V> map = new TreeMap<>();
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> TreeMap<K, V> toTreeMap(
V[] c,
Function<? super V, K> keyGenerator,
Comparator<? super K> keyComparator) {
TreeMap<K, V> map = new TreeMap<>(keyComparator);
fillIntoEmptyMap(map, c, keyGenerator);
return map;
}
public static <K, V> void fillIntoEmptyMap(
Map<K, ? super V> map, V[] c,
Function<? super V, K> keyGenerator) {
fillIntoEmptyMap(map, Arrays.asList(c), keyGenerator);
}
public static <R, C, V> Table<R, C, V> synchronizedTable(Table<R, C, V> t) {
if (t instanceof SynchronizedTable) {
return t;
} else {
return SynchronizedTable.of(t);
}
}
private MoreCollections() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-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.
@@ -16,20 +16,18 @@
package xyz.zhouxy.plusone.commons.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Numbers
* NumberUtil
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
*/
public class Numbers {
// #region - sum
private Numbers() {
throw new IllegalStateException("Utility class");
}
// sum
public static int sum(final short... numbers) {
int result = 0;
@@ -71,61 +69,25 @@ public class Numbers {
return result;
}
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(short value, short min, short max) {
return value >= min && value < max;
}
public static BigDecimal sum(final BigDecimal... numbers) {
return BigDecimals.sum(numbers);
public static boolean between(int value, int min, int max) {
return value >= min && value < max;
}
// #endregion
// #region - nullToZero
public static short nullToZero(@Nullable final Short val) {
return val != null ? val : 0;
public static boolean between(long value, long min, long max) {
return value >= min && value < max;
}
public static int nullToZero(@Nullable final Integer val) {
return val != null ? val : 0;
public static boolean between(float value, float min, float max) {
return value >= min && value < max;
}
public static long nullToZero(@Nullable final Long val) {
return val != null ? val : 0L;
}
public static float nullToZero(@Nullable final Float val) {
return val != null ? val : 0.0F;
}
public static double nullToZero(@Nullable final Double val) {
return val != null ? val : 0.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");
public static boolean between(double value, double min, double max) {
return value >= min && value < max;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -31,14 +31,14 @@ import com.google.common.annotations.Beta;
* <p>
* 提供一些 Optional 相关的方法
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @since 0.1.0
* @see Optional
* @see OptionalInt
* @see OptionalLong
* @see OptionalDouble
*/
public class OptionalTools {
public class OptionalUtil {
/**
* 将包装类 {@link Integer} 转为 {@link OptionalInt}not null
@@ -46,7 +46,7 @@ public class OptionalTools {
* 包装类为 {@code null} 表示值的缺失转为 {@link OptionalInt}
* {@link OptionalInt#empty()} 表示值的缺失
* </p>
*
*
* @param value 包装对象
* @return {@link OptionalInt} 实例
*/
@@ -59,12 +59,12 @@ public class OptionalTools {
* <p>
* {@code Optional<Integer>} 将整数包装了两次改为使用 {@link OptionalInt} 包装其中的整数数据
* </p>
*
*
* @param optionalObj {@code Optional<Integer>} 对象
* @return {@link OptionalInt} 实例
*/
public static OptionalInt toOptionalInt(Optional<Integer> optionalObj) {
return optionalObj.isPresent() ? OptionalInt.of(optionalObj.get()) : OptionalInt.empty();
return optionalOf(optionalObj.orElse(null));
}
/**
@@ -73,7 +73,7 @@ public class OptionalTools {
* 包装类为 {@code null} 表示值的缺失转为 {@link OptionalLong}
* {@link OptionalLong#empty()} 表示值的缺失
* </p>
*
*
* @param value 包装对象
* @return {@link OptionalLong} 实例
*/
@@ -86,12 +86,12 @@ public class OptionalTools {
* <p>
* {@code Optional<Long>} 将整数包装了两次改为使用 {@link OptionalLong} 包装其中的整数数据
* </p>
*
*
* @param optionalObj 包装对象
* @return {@link OptionalLong} 实例
*/
public static OptionalLong toOptionalLong(Optional<Long> optionalObj) {
return optionalObj.isPresent() ? OptionalLong.of(optionalObj.get()) : OptionalLong.empty();
return optionalOf(optionalObj.orElse(null));
}
/**
@@ -100,7 +100,7 @@ public class OptionalTools {
* 包装类为 {@code null} 表示值的缺失转为 {@link OptionalDouble}
* {@link OptionalDouble#empty()} 表示值的缺失
* </p>
*
*
* @param value 包装对象
* @return {@link OptionalDouble} 实例
*/
@@ -113,18 +113,18 @@ public class OptionalTools {
* <p>
* {@code Optional<Double>} 将整数包装了两次改为使用 {@link OptionalDouble} 包装其中的整数数据
* </p>
*
*
* @param optionalObj 包装对象
* @return {@link OptionalDouble} 实例
*/
public static OptionalDouble toOptionalDouble(Optional<Double> optionalObj) {
return optionalObj.isPresent() ? OptionalDouble.of(optionalObj.get()) : OptionalDouble.empty();
return optionalOf(optionalObj.orElse(null));
}
/**
* return the value of the optional object if present,
* otherwise {@code null}.
*
*
* @param <T> the class of the value
* @param optionalObj {@link Optional} object, which must be non-null.
* @return the value of the optional object if present, otherwise {@code null}.
@@ -149,7 +149,7 @@ public class OptionalTools {
return optionalObj.isPresent() ? optionalObj.getAsDouble() : null;
}
private OptionalTools() {
private OptionalUtil() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2022-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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package xyz.zhouxy.plusone.commons.model.dto;
package xyz.zhouxy.plusone.commons.util;
import java.util.List;
@@ -27,24 +27,24 @@ import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
*
* @param <T> 内容列表的元素类型
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @see PagingAndSortingQueryParams
*/
public class PageResult<T> {
public class PageDTO<T> {
private final long total;
private final List<T> content;
private PageResult(List<T> content, long total) {
private PageDTO(List<T> content, long total) {
Preconditions.checkNotNull(content, "Content must not be null.");
this.content = content;
this.total = total;
}
@StaticFactoryMethod(PageResult.class)
public static <T> PageResult<T> of(List<T> content, long total) {
return new PageResult<>(content, total);
@StaticFactoryMethod(PageDTO.class)
public static <T> PageDTO<T> of(List<T> content, long total) {
return new PageDTO<>(content, total);
}
public long getTotal() {

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.util;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import xyz.zhouxy.plusone.commons.annotation.Overridable;
/**
* 分页排序查询参数
*
* <p>
* 根据传入的 {@code size} 和 {@code pageNum}
* 提供 {@code getOffset} 方法计算 SQL 语句中 {@code offset} 的值。
* </p>
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @see PageDTO
*/
public class PagingAndSortingQueryParams {
private static final int DEFAULT_PAGE_SIZE = 15;
private int size;
private long pageNum;
private final List<String> orderBy = new LinkedList<>();
private final Set<String> sortableProperties;
public PagingAndSortingQueryParams() {
this.sortableProperties = Collections.emptySet();
}
public PagingAndSortingQueryParams(String... sortableProperties) {
for (String p : sortableProperties) {
Preconditions.checkArgument(StringUtils.isNotBlank(p), "Property name must not be blank.");
}
this.sortableProperties = ImmutableSet.<String>builder().add(sortableProperties).build();
}
// Getters
public final List<String> getOrderBy() {
return Collections.unmodifiableList(this.orderBy);
}
public final int getSize() {
return this.size;
}
public final long getPageNum() {
return this.pageNum;
}
public final long getOffset() {
return (this.pageNum - 1) * this.size;
}
// Getters end
// Setters
public final void setOrderBy(@Nullable List<String> properties) {
this.orderBy.clear();
if (properties != null && !properties.isEmpty()) {
for (String colName : properties) {
Preconditions.checkArgument(this.sortableProperties.contains(colName),
"The property name must be in the set of sortable properties.");
}
this.orderBy.addAll(properties);
}
}
public final void setSize(@Nullable Integer size) {
this.size = size != null ? size : getDefaultSize();
}
public final void setPageNum(@Nullable Long pageNum) {
this.pageNum = pageNum != null ? pageNum : 1L;
}
// Setters end
@Overridable
protected int getDefaultSize() {
return DEFAULT_PAGE_SIZE;
}
@Override
public String toString() {
return "PagingAndSortingQueryParams [size=" + size + ", pageNum=" + pageNum + ", orderBy=" + orderBy
+ ", sortableProperties=" + sortableProperties + "]";
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.util;
import java.util.function.Supplier;
import com.google.common.base.Preconditions;
/**
* Guava Preconditions 的扩展。
*
* @author ZhouXY
*
* @see com.google.common.base.Preconditions
*/
public class PreconditionsExt {
public static <E extends Throwable> void check(boolean condition, Supplier<E> e) throws E {
if (!condition) {
throw e.get();
}
}
public static <T, E extends Throwable> void checkAllNotNull(Iterable<T> values) throws E {
Preconditions.checkNotNull(values);
for (T item : values) {
Preconditions.checkNotNull(item);
}
}
@SafeVarargs
public static <T, E extends Throwable> void checkAllNotNull(T... values) throws E {
Preconditions.checkNotNull(values);
for (T item : values) {
Preconditions.checkNotNull(item);
}
}
private PreconditionsExt() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -1,136 +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.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nonnull;
public final class RandomTools {
public static final SecureRandom DEFAULT_SECURE_RANDOM = new SecureRandom();
public static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz";
public static final String NUMBERS = "0123456789";
/**
* 使用传入的随机数生成器,生成指定长度的字符串
*
* @param random 随机数生成器。根据需要可以传入
* {@link java.util.concurrent.ThreadLocalRandom}、{@link java.security.SecureRandom}
* 等,不为空
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
* @param length 字符串长度
* @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(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(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(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(DEFAULT_SECURE_RANDOM, sourceCharacters, length);
}
/**
* 使用传入的随机数生成器,生成指定长度的字符串
*
* @param random 随机数生成器。根据需要可以传入
* {@link java.util.concurrent.ThreadLocalRandom}、{@link java.security.SecureRandom}
* 等,不为空
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
* @param length 字符串长度
* @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(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(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(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(DEFAULT_SECURE_RANDOM, sourceCharacters, length);
}
/**
* 使用传入的随机数生成器,生成指定长度的字符串
*
* @param random 随机数生成器。根据需要可以传入
* {@link java.util.concurrent.ThreadLocalRandom}、{@link java.security.SecureRandom}
* 等,不为空
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
* @param length 字符串长度
* @return 随机字符串
*/
private static String randomStrInternal(@Nonnull Random random, @Nonnull char[] sourceCharacters, int length) {
if (length == 0) {
return StringTools.EMPTY_STRING;
}
final char[] result = new char[length];
for (int i = 0; i < length; i++) {
result[i] = sourceCharacters[random.nextInt(sourceCharacters.length)];
}
return String.valueOf(result);
}
/**
* 使用传入的随机数生成器,生成指定长度的字符串
*
* @param random 随机数生成器。根据需要可以传入
* {@link java.util.concurrent.ThreadLocalRandom}、{@link java.security.SecureRandom}
* 等,不为空
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
* @param length 字符串长度
* @return 随机字符串
*/
private static String randomStrInternal(@Nonnull Random random, @Nonnull String sourceCharacters, int length) {
if (length == 0) {
return StringTools.EMPTY_STRING;
}
final char[] result = new char[length];
for (int i = 0; i < length; i++) {
result[i] = sourceCharacters.charAt(random.nextInt(sourceCharacters.length()));
}
return String.valueOf(result);
}
private RandomTools() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -0,0 +1,18 @@
package xyz.zhouxy.plusone.commons.util;
import java.security.SecureRandom;
public final class RandomUtil {
private RandomUtil() {
throw new IllegalStateException("Utility class");
}
public static String secureRandomStr(char[] sourceCharacters, int length) {
SecureRandom random = new SecureRandom();
char[] result = new char[length];
for (int i = 0; i < length; i++) {
result[i] = sourceCharacters[random.nextInt(sourceCharacters.length)];
}
return String.valueOf(result);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2022-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.
@@ -19,7 +19,6 @@ package xyz.zhouxy.plusone.commons.util;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -28,72 +27,74 @@ import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import xyz.zhouxy.plusone.commons.collection.SafeConcurrentHashMap;
/**
* 封装一些常用的正则操作并可以缓存 {@link Pattern} 实例以复用最多缓存大概 256
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*
* @author ZhouXY
*
*/
public final class RegexTools {
public final class RegexUtil {
private static final int DEFAULT_CACHE_INITIAL_CAPACITY = 64;
private static final int MAX_CACHE_SIZE = 256;
private static final Map<String, Pattern> PATTERN_CACHE
= new ConcurrentHashMap<>(DEFAULT_CACHE_INITIAL_CAPACITY);
= new SafeConcurrentHashMap<>(DEFAULT_CACHE_INITIAL_CAPACITY);
/**
* 获取 {@link Pattern} 实例
*
*
* @param pattern 正则表达式
* @param cachePattern 是否缓存 {@link Pattern} 实例
* @return {@link Pattern} 实例
*/
public static Pattern getPattern(final String pattern, final boolean cachePattern) {
Preconditions.checkNotNull(pattern);
return cachePattern ? cacheAndGetPatternInternal(pattern) : getPatternInternal(pattern);
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
return cachePattern ? getAndCachePatternInternal(pattern) : getPatternInternal(pattern);
}
/**
* 获取 {@link Pattern} 实例不缓存
*
*
* @param pattern 正则表达式
* @return {@link Pattern} 实例
*/
public static Pattern getPattern(final String pattern) {
Preconditions.checkNotNull(pattern);
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
return getPatternInternal(pattern);
}
/**
* 将各个正则表达式转为 {@link Pattern} 实例
*
*
* @param patterns 正则表达式
* @param cachePattern 是否缓存 {@link Pattern} 实例
* @return {@link Pattern} 实例数组
*/
public static Pattern[] getPatterns(final String[] patterns, final boolean cachePattern) {
Preconditions.checkNotNull(patterns);
Preconditions.checkArgument(allNotNull(patterns));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
return cachePattern
? cacheAndGetPatternsInternal(patterns)
? getAndCachePatternsInternal(patterns)
: getPatternsInternal(patterns);
}
/**
* 将各个正则表达式转为 {@link Pattern} 实例不缓存
*
*
* @param patterns 正则表达式
* @return {@link Pattern} 实例数组
*/
public static Pattern[] getPatterns(final String[] patterns) {
Preconditions.checkNotNull(patterns);
Preconditions.checkArgument(allNotNull(patterns));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
return getPatternsInternal(patterns);
}
/**
* 手动缓存 Pattern 实例
*
*
* @param pattern 要缓存的 {@link Pattern} 实例
* @return 缓存的 Pattern 实例如果缓存已满则返回 {@code null}
*/
@@ -109,45 +110,45 @@ public final class RegexTools {
/**
* 判断 {@code input} 是否匹配 {@code pattern}
*
*
* @param input 输入
* @param pattern 正则
* @return 判断结果
*/
public static boolean matches(@Nullable final CharSequence input, final Pattern pattern) {
Preconditions.checkNotNull(pattern);
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
return matchesInternal(input, pattern);
}
/**
* 判断 {@code input} 是否匹配 {@code patterns} 中的一个
*
*
* @param input 输入
* @param patterns 正则
* @return 判断结果
*/
public static boolean matchesOne(@Nullable final CharSequence input, final Pattern[] patterns) {
Preconditions.checkNotNull(patterns);
Preconditions.checkArgument(allNotNull(patterns));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
return matchesOneInternal(input, patterns);
}
/**
* 判断 {@code input} 是否匹配全部正则
*
*
* @param input 输入
* @param patterns 正则
* @return 判断结果
*/
public static boolean matchesAll(@Nullable final CharSequence input, final Pattern[] patterns) {
Preconditions.checkNotNull(patterns);
Preconditions.checkArgument(allNotNull(patterns));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
return matchesAllInternal(input, patterns);
}
/**
* 判断 {@code input} 是否匹配 {@code pattern}
*
*
* @param input 输入
* @param pattern 正则表达式
* @param cachePattern 是否缓存 {@link Pattern} 实例
@@ -155,28 +156,28 @@ public final class RegexTools {
*/
public static boolean matches(@Nullable final CharSequence input, final String pattern,
final boolean cachePattern) {
Preconditions.checkNotNull(pattern);
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
Pattern p = cachePattern
? cacheAndGetPatternInternal(pattern)
? getAndCachePatternInternal(pattern)
: getPatternInternal(pattern);
return matchesInternal(input, p);
}
/**
* 判断 {@code input} 是否匹配 {@code pattern}不缓存 {@link Pattern} 实例
*
*
* @param input 输入
* @param pattern 正则表达式
* @return 判断结果
*/
public static boolean matches(@Nullable final CharSequence input, final String pattern) {
Preconditions.checkNotNull(pattern);
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
return matchesInternal(input, getPatternInternal(pattern));
}
/**
* 判断 {@code input} 是否匹配 {@code patterns} 中的一个
*
*
* @param input 输入
* @param patterns 正则表达式
* @param cachePattern 是否缓存 {@link Pattern} 实例
@@ -184,31 +185,31 @@ 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));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
final Pattern[] patternSet = cachePattern
? cacheAndGetPatternsInternal(patterns)
? getAndCachePatternsInternal(patterns)
: getPatternsInternal(patterns);
return matchesOneInternal(input, patternSet);
}
/**
* 判断 {@code input} 是否匹配 {@code patterns} 中的一个不缓存 {@link Pattern} 实例
*
*
* @param input 输入
* @param patterns 正则表达式
* @return 判断结果
*/
public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns) {
Preconditions.checkNotNull(patterns);
Preconditions.checkArgument(allNotNull(patterns));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
final Pattern[] patternSet = getPatternsInternal(patterns);
return matchesOneInternal(input, patternSet);
}
/**
* 判断 {@code input} 是否匹配全部正则
*
*
* @param input 输入
* @param patterns 正则表达式
* @param cachePattern 是否缓存 {@link Pattern} 实例
@@ -216,68 +217,68 @@ 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));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
final Pattern[] patternSet = cachePattern
? cacheAndGetPatternsInternal(patterns)
? getAndCachePatternsInternal(patterns)
: getPatternsInternal(patterns);
return matchesAllInternal(input, patternSet);
}
/**
* 判断 {@code input} 是否匹配全部正则不缓存 {@link Pattern} 实例
*
*
* @param input 输入
* @param patterns 正则表达式
* @return 判断结果
*/
public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns) {
Preconditions.checkNotNull(patterns);
Preconditions.checkArgument(allNotNull(patterns));
Preconditions.checkNotNull(patterns, "Patterns can not be null.");
Preconditions.checkArgument(allNotNull(patterns), "The pattern can not be null.");
final Pattern[] patternSet = getPatternsInternal(patterns);
return matchesAllInternal(input, patternSet);
}
/**
* 生成 Matcher
*
*
* @param input 输入
* @param pattern 正则
* @return 结果
*/
public static Matcher getMatcher(final CharSequence input, final Pattern pattern) {
Preconditions.checkNotNull(input);
Preconditions.checkNotNull(pattern);
Preconditions.checkNotNull(input, "The input can not be null.");
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
return pattern.matcher(input);
}
/**
* 生成 Matcher
*
*
* @param input 输入
* @param pattern 正则表达式
* @param cachePattern 是否缓存 {@link Pattern} 实例
* @return 结果
*/
public static Matcher getMatcher(final CharSequence input, final String pattern, boolean cachePattern) {
Preconditions.checkNotNull(input);
Preconditions.checkNotNull(pattern);
Preconditions.checkNotNull(input, "The input can not be null.");
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
final Pattern p = cachePattern
? cacheAndGetPatternInternal(pattern)
? getAndCachePatternInternal(pattern)
: getPatternInternal(pattern);
return p.matcher(input);
}
/**
* 生成 Matcher不缓存 {@link Pattern} 实例
*
*
* @param input 输入
* @param pattern 正则表达式
* @return 结果
*/
public static Matcher getMatcher(final CharSequence input, final String pattern) {
Preconditions.checkNotNull(input);
Preconditions.checkNotNull(pattern);
Preconditions.checkNotNull(input, "The input can not be null.");
Preconditions.checkNotNull(pattern, "The pattern can not be null.");
return getPatternInternal(pattern).matcher(input);
}
@@ -285,13 +286,14 @@ public final class RegexTools {
/**
* 获取 {@link Pattern} 实例
*
*
* @param pattern 正则表达式
* @param cachePattern 是否缓存 {@link Pattern} 实例
* @return {@link Pattern} 实例
*/
@SuppressWarnings("null")
@Nonnull
private static Pattern cacheAndGetPatternInternal(@Nonnull final String pattern) {
private static Pattern getAndCachePatternInternal(@Nonnull final String pattern) {
if (PATTERN_CACHE.size() < MAX_CACHE_SIZE) {
return PATTERN_CACHE.computeIfAbsent(pattern, Pattern::compile);
}
@@ -304,10 +306,11 @@ public final class RegexTools {
/**
* 获取 {@link Pattern} 实例不缓存
*
*
* @param pattern 正则表达式
* @return {@link Pattern} 实例
*/
@SuppressWarnings("null")
@Nonnull
private static Pattern getPatternInternal(@Nonnull final String pattern) {
Pattern result = PATTERN_CACHE.get(pattern);
@@ -319,33 +322,35 @@ public final class RegexTools {
/**
* 将各个正则表达式转为 {@link Pattern} 实例
*
*
* @param patterns 正则表达式
* @return {@link Pattern} 实例数组
*/
@SuppressWarnings("null")
@Nonnull
private static Pattern[] cacheAndGetPatternsInternal(@Nonnull final String[] patterns) {
private static Pattern[] getAndCachePatternsInternal(@Nonnull final String[] patterns) {
return Arrays.stream(patterns)
.map(RegexTools::cacheAndGetPatternInternal)
.map(RegexUtil::getAndCachePatternInternal)
.toArray(Pattern[]::new);
}
/**
* 将各个正则表达式转为 {@link Pattern} 实例
*
*
* @param patterns 正则表达式
* @return {@link Pattern} 实例数组
*/
@SuppressWarnings("null")
@Nonnull
private static Pattern[] getPatternsInternal(@Nonnull final String[] patterns) {
return Arrays.stream(patterns)
.map(RegexTools::getPatternInternal)
.map(RegexUtil::getPatternInternal)
.toArray(Pattern[]::new);
}
/**
* 判断 {@code input} 是否匹配 {@code pattern}
*
*
* @param input 输入
* @param pattern 正则
* @return 判断结果
@@ -354,23 +359,37 @@ public final class RegexTools {
return input != null && pattern.matcher(input).matches();
}
@SuppressWarnings("null")
private static boolean matchesOneInternal(@Nullable final CharSequence input, @Nonnull final Pattern[] patterns) {
return input != null
&& Arrays.stream(patterns)
.anyMatch(pattern -> pattern.matcher(input).matches());
if (input == null) {
return false;
}
for (Pattern pattern : patterns) {
if (matchesInternal(input, pattern)) {
return true;
}
}
return false;
}
private static boolean matchesAllInternal(@Nullable final CharSequence input, @Nonnull final Pattern[] patterns) {
return input != null
&& Arrays.stream(patterns)
.allMatch(pattern -> pattern.matcher(input).matches());
@SuppressWarnings("null")
private static boolean matchesAllInternal(final CharSequence input, final Pattern[] patterns) {
if (input == null) {
return false;
}
for (Pattern pattern : patterns) {
if (!matchesInternal(input, pattern)) {
return false;
}
}
return true;
}
private static <T> boolean allNotNull(T[] array) {
return Arrays.stream(array).allMatch(Objects::nonNull);
}
private RegexTools() {
private RegexUtil() {
// 不允许实例化
throw new IllegalStateException("Utility class");
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.util;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.common.base.Preconditions;
/**
* 对返回给前端的数据进行封装
*
* @author <a href="https://gitee.com/zhouxy108">ZhouXY</a>
* @deprecated 已被 {@link UnifiedResponse} 代替。
*/
@Deprecated
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestfulResult {
public static final int SUCCESS_STATUS = 2000000;
public static final int DEFAULT_ERROR_STATUS = 9999999;
private final Object status;
private final String message;
private final @Nullable Object data;
private RestfulResult(final Object status, final String message) {
this(status, message, null);
}
public static RestfulResult success() {
return new RestfulResult(SUCCESS_STATUS, "操作成功");
}
public static RestfulResult success(final String message) {
Preconditions.checkNotNull(message, "Message must not be null.");
return new RestfulResult(SUCCESS_STATUS, message);
}
public static RestfulResult success(final String message, @Nullable final Object data) {
Preconditions.checkNotNull(message, "Message must not be null.");
return new RestfulResult(SUCCESS_STATUS, message, data);
}
public static RestfulResult error() {
return new RestfulResult(DEFAULT_ERROR_STATUS, "未知错误");
}
public static RestfulResult error(final Object status, final String message) {
Preconditions.checkNotNull(status, "Status must not be null.");
Preconditions.checkNotNull(message, "Message must not be null.");
return new RestfulResult(status, message);
}
public static RestfulResult error(final Object status, final String message, @Nullable final Object data) {
Preconditions.checkNotNull(status, "Status must not be null.");
Preconditions.checkNotNull(message, "Message must not be null.");
return new RestfulResult(status, message, data);
}
public static RestfulResult error(final Object status, final Throwable e) {
Preconditions.checkNotNull(status, "Status must not be null.");
Preconditions.checkNotNull(e, "Exception must not be null.");
String msg = e.getMessage();
if (msg == null) {
msg = "";
}
return new RestfulResult(status, msg);
}
public static RestfulResult of(final boolean isSuccess,
final Supplier<RestfulResult> success, final Supplier<RestfulResult> error) {
Preconditions.checkNotNull(success, "Success supplier must not be null.");
Preconditions.checkNotNull(error, "Error supplier must not be null.");
return isSuccess ? success.get() : error.get();
}
public static RestfulResult of(final BooleanSupplier isSuccess,
final Supplier<RestfulResult> success, final Supplier<RestfulResult> error) {
Preconditions.checkNotNull(isSuccess, "Conditions for success must not be null.");
Preconditions.checkNotNull(success, "Success supplier must not be null.");
Preconditions.checkNotNull(error, "Error supplier must not be null.");
return isSuccess.getAsBoolean() ? success.get() : error.get();
}
// Constructors
private RestfulResult(final Object status, final String message, @Nullable final Object data) {
this.status = status;
this.message = message;
this.data = data;
}
// Constructors end
// Getters
public Object getStatus() {
return status;
}
public String getMessage() {
return message;
}
@Nullable
public Object getData() {
return data;
}
// Getters end
@Override
public String toString() {
return "RestfulResult [status=" + status + ", message=" + message + ", data=" + data + "]";
}
}

View File

@@ -1,19 +1,3 @@
/*
* 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.concurrent.TimeUnit;
@@ -80,9 +64,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),
Preconditions.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),
Preconditions.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);

View File

@@ -1,50 +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 com.google.common.annotations.Beta;
@Beta
public class StringTools {
public static final String EMPTY_STRING = "";
public static boolean isNotBlank(final String cs) {
if (cs == null || cs.isEmpty()) {
return false;
}
for (int i = 0; i < cs.length(); i++) {
if (!Character.isWhitespace(cs.charAt(i))) {
return true;
}
}
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.checkArgumentNotNull(str);
return String.valueOf(ArrayTools.repeat(str.toCharArray(), times, maxLength));
}
private StringTools() {
throw new IllegalStateException("Utility class");
}
}

View File

@@ -0,0 +1,25 @@
package xyz.zhouxy.plusone.commons.util;
/**
* 成功结果
*
* @author zhouxy
*/
final class SuccessResult extends UnifiedResponse {
private static final String SUCCESS_STATUS = "000000";
private static final String DEFAULT_SUCCESS_MSG = "SUCCESS";
SuccessResult() {
super(SUCCESS_STATUS, DEFAULT_SUCCESS_MSG);
}
SuccessResult(String message) {
super(SUCCESS_STATUS, message);
}
SuccessResult(String message, Object data) {
super(SUCCESS_STATUS, message, data);
}
private static final long serialVersionUID = -7509096647748429661L;
}

View File

@@ -1,23 +1,6 @@
/*
* 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.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -25,96 +8,33 @@ import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
/**
* TreeBuilder
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0
*/
public class TreeBuilder<T, TSubTree extends T, TIdentity> {
private final Collection<T> nodes;
private final Function<T, TIdentity> identityGetter;
private final Function<T, Optional<TIdentity>> parentIdentityGetter;
private final BiConsumer<TSubTree, T> addChildMethod;
private final Comparator<? super T> defaultComparator;
private final BiConsumer<TSubTree, T> addChildrenMethod;
public TreeBuilder(Function<T, TIdentity> identityGetter, Function<T, Optional<TIdentity>> parentIdentityGetter,
BiConsumer<TSubTree, T> addChild) {
this(identityGetter, parentIdentityGetter, addChild, null);
}
public TreeBuilder(Function<T, TIdentity> identityGetter, Function<T, Optional<TIdentity>> parentIdentityGetter,
BiConsumer<TSubTree, T> addChild, Comparator<? super T> defaultComparator) {
public TreeBuilder(Collection<T> nodes, Function<T, TIdentity> identityGetter,
Function<T, Optional<TIdentity>> parentIdentityGetter, BiConsumer<TSubTree, T> addChildren) {
this.nodes = nodes;
this.identityGetter = identityGetter;
this.parentIdentityGetter = parentIdentityGetter;
this.addChildMethod = addChild;
this.defaultComparator = defaultComparator;
this.addChildrenMethod = addChildren;
}
/**
* 将节点构建成树。使用 {@link #defaultComparator} 进行排序。如果 {@link #defaultComparator}
* <p>
* <b>注意,该方法会直接操作 nodes 列表中的节点,并没有做深拷贝,
* 注意避免 nodes 中的元素产生变化所带来的意料之外的影响。</b>
*
* @param nodes 平铺的节点列表
*/
public List<T> buildTree(Collection<T> nodes) {
Preconditions.checkNotNull(nodes);
return buildTreeInternal(nodes, this.defaultComparator);
}
/**
* 将节点构建成树。
* <p>
* <b>!!注意:该方法会直接操作 nodes 列表中的节点,并没有做深拷贝,
* 注意避免 nodes 中的元素产生变化所带来的意料之外的影响。</b>
*
* @param nodes 平铺的节点列表
* @param comparator 用于节点的排序。
* 若为 {@code null},则使用 {@link #defaultComparator}
* 若 {@link #defaultComparator} 也为 {@code null},则不排序。
* <b>仅影响调用 addChild 的顺序,如果操作对象本身对应的控制了子节点的顺序,无法影响其相关逻辑。</b>
*/
public List<T> buildTree(Collection<T> nodes, @Nullable Comparator<? super T> comparator) {
Preconditions.checkNotNull(nodes);
final Comparator<? super T> c = (comparator != null) ? comparator : this.defaultComparator;
return buildTreeInternal(nodes, c);
}
/**
* 将节点构建成树。
* <p>
* <b>注意,该方法会直接操作 nodes 列表中的节点,并没有做深拷贝,
* 注意避免 nodes 中的元素产生变化所带来的意料之外的影响。</b>
*
* @param nodes 平铺的节点列表
* @param comparator 用于节点的排序。若为 {@code null},则不排序
*/
private List<T> buildTreeInternal(Collection<T> nodes, @Nullable Comparator<? super T> comparator) {
final Collection<T> allNodes;
if (comparator == null) {
allNodes = nodes;
} else {
allNodes = nodes.stream().sorted(comparator).collect(Collectors.toList());
}
final Map<TIdentity, T> identityNodeMap = allNodes.stream()
.collect(Collectors.toMap(identityGetter, Function.identity(), (n1, n2) -> n1));
// 根节点
final List<T> rootNodes = allNodes.stream()
public List<T> buildTree() {
Map<TIdentity, T> identityNodeMap = MoreCollections.toHashMap(nodes, identityGetter);
List<T> result = this.nodes.stream()
.filter(node -> !this.parentIdentityGetter.apply(node).isPresent())
.collect(Collectors.toList());
allNodes.forEach(node -> parentIdentityGetter.apply(node).ifPresent(parentIdentity -> {
if (identityNodeMap.containsKey(parentIdentity)) {
@SuppressWarnings("unchecked")
TSubTree parentNode = (TSubTree) identityNodeMap.get(parentIdentity);
addChildMethod.accept(parentNode, node);
for (T node : this.nodes) {
Optional<TIdentity> parentIdentity = parentIdentityGetter.apply(node);
if (parentIdentity.isPresent() && identityNodeMap.containsKey(parentIdentity.get())) {
@SuppressWarnings("all")
TSubTree parentNode = (TSubTree) identityNodeMap.get(parentIdentity.get());
addChildrenMethod.accept(parentNode, node);
}
}));
return rootNodes;
}
return result;
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright 2022-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 xyz.zhouxy.plusone.commons.util;
import xyz.zhouxy.plusone.commons.annotation.UnsupportedOperation;
import xyz.zhouxy.plusone.commons.base.IWithCode;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.base.Preconditions;
/**
* 统一结果,对返回给前端的数据进行封装。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public abstract class UnifiedResponse extends HashMap<String, Object> {
private static final long serialVersionUID = -6198220274571286031L;
private static final String STATUS_KEY = "status";
private static final String MESSAGE_KEY = "message";
private static final String DATA_KEY = "data";
public static UnifiedResponse success() {
return new SuccessResult();
}
public static UnifiedResponse success(String message) {
return new SuccessResult(message);
}
public static UnifiedResponse success(String message, Object data) {
return new SuccessResult(message, data);
}
public static UnifiedResponse error(String message) {
return new ErrorResult(message);
}
public static UnifiedResponse error(String message, Object data) {
return new ErrorResult(message, data);
}
public static UnifiedResponse error(Object status, String message) {
return new ErrorResult(status, message);
}
public static UnifiedResponse error(Object status, String message, Object data) {
return new ErrorResult(status, message, data);
}
public static UnifiedResponse error(Object status, Throwable e) {
return new ErrorResult(status, e);
}
public static <E extends Throwable & IWithCode<?>> UnifiedResponse error(E e) {
return new ErrorResult(e);
}
public static UnifiedResponse of(Object status, String message) {
return new CustomResult(status, message);
}
public static UnifiedResponse of(Object status, String message, Object data) {
return new CustomResult(status, message, data);
}
public static UnifiedResponse of(final boolean isSuccess,
final Supplier<UnifiedResponse> success, final Supplier<UnifiedResponse> error) {
Preconditions.checkNotNull(success, "Success supplier must not be null.");
Preconditions.checkNotNull(error, "Error supplier must not be null.");
return isSuccess ? success.get() : error.get();
}
public static UnifiedResponse of(final BooleanSupplier isSuccess,
final Supplier<UnifiedResponse> success, final Supplier<UnifiedResponse> error) {
Preconditions.checkNotNull(isSuccess, "Conditions for success must not be null.");
Preconditions.checkNotNull(success, "Success supplier must not be null.");
Preconditions.checkNotNull(error, "Error supplier must not be null.");
return isSuccess.getAsBoolean() ? success.get() : error.get();
}
protected UnifiedResponse(Object status, String message) {
setStatus(status);
setMessage(message);
}
protected UnifiedResponse(Object status, String message, Object data) {
setStatus(status);
setMessage(message);
setData(data);
}
private void setStatus(Object status) {
Objects.requireNonNull(status);
if (status instanceof String) {
super.put(STATUS_KEY, status);
} else {
super.put(STATUS_KEY, status.toString());
}
}
private void setData(Object data) {
super.put(DATA_KEY, Objects.requireNonNull(data));
}
private void setMessage(String message) {
super.put(MESSAGE_KEY, Objects.requireNonNull(message));
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object put(String key, Object value) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public void putAll(Map<? extends String, ?> m) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public void clear() {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object putIfAbsent(String key, Object value) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public boolean replace(String key, Object oldValue, Object newValue) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object replace(String key, Object value) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object computeIfAbsent(String key, Function<? super String, ?> mappingFunction) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object computeIfPresent(String key, BiFunction<? super String, ? super Object, ?> remappingFunction) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object compute(String key, BiFunction<? super String, ? super Object, ?> remappingFunction) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public Object merge(String key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation.
*/
@UnsupportedOperation
@Deprecated
@Override
public void replaceAll(BiFunction<? super String, ? super Object, ?> function) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,19 +1,3 @@
/*
* 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;
import static org.junit.jupiter.api.Assertions.assertSame;
@@ -27,7 +11,6 @@ import xyz.zhouxy.plusone.commons.util.Enumeration;
import java.util.ArrayList;
import java.util.Collection;
@SuppressWarnings("deprecation")
class EnumerationTests {
private static final Logger log = LoggerFactory.getLogger(EnumerationTests.class);
@@ -45,7 +28,6 @@ class EnumerationTests {
}
}
@SuppressWarnings("deprecation")
final class EntityStatus extends Enumeration<EntityStatus> {
private EntityStatus(int value, String name) {
@@ -56,7 +38,7 @@ final class EntityStatus extends Enumeration<EntityStatus> {
public static final EntityStatus AVAILABLE = new EntityStatus(0, "正常");
public static final EntityStatus DISABLED = new EntityStatus(1, "禁用");
private static final ValueSet<EntityStatus> VALUE_SET = ValueSet.of(new EntityStatus[] { AVAILABLE, DISABLED });
private static final ValueSet<EntityStatus> VALUE_SET = ValueSet.of(AVAILABLE, DISABLED);
public static EntityStatus of(int value) {
return VALUE_SET.get(value);
@@ -67,7 +49,6 @@ final class EntityStatus extends Enumeration<EntityStatus> {
}
}
@SuppressWarnings("deprecation")
final class Result extends Enumeration<Result> {
private Result(int id, String name) {
super(id, name);
@@ -76,7 +57,7 @@ final class Result extends Enumeration<Result> {
public static final Result SUCCESSFUL = new Result(1, "成功");
public static final Result FAILURE = new Result(0, "失败");
private static final ValueSet<Result> VALUE_SET = ValueSet.of(new Result[] { SUCCESSFUL, FAILURE });
private static final ValueSet<Result> VALUE_SET = ValueSet.of(SUCCESSFUL, FAILURE);
public static Result of(int id) {
return VALUE_SET.get(id);

View File

@@ -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;
import java.io.ObjectStreamClass;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.system.NoAvailableMacFoundException;
@Slf4j
class SerialTests {
@Test
void testSerialVersionUID() {
long uid = getSerialVersionUID(NoAvailableMacFoundException.class);
log.info("\n private static final long serialVersionUID = {}L;", uid);
}
private long getSerialVersionUID(Class<?> cl) {
ObjectStreamClass c = ObjectStreamClass.lookup(cl);
return c.getSerialVersionUID();
}
}

View File

@@ -1,104 +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 static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class RefTests {
@Test
void testRef() {
Ref<String> strRef = new Ref<>("ZhouXY");
apply(strRef);
assertEquals("Hello ZhouXY", strRef.getValue());
log.info("strRef: {}", strRef);
}
void apply(Ref<String> 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);
}
}

View File

@@ -0,0 +1,37 @@
package xyz.zhouxy.plusone.commons.domain;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.annotation.ValueObject;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
class ValidatableStringRecordTests {
private static final Logger log = LoggerFactory.getLogger(ValidatableStringRecordTests.class);
@Test
void test() {
Username username = Username.of("ZhouXY");
assertNotNull(username);
String usernameStr = username.value();
assertNotNull(usernameStr);
log.info("usernameStr: {}", usernameStr);
}
}
@ValueObject
class Username extends ValidatableStringRecord {
private Username(String username) {
super(username, PatternConsts.USERNAME);
}
@StaticFactoryMethod(Username.class)
public static Username of(String username) {
return new Username(username);
}
}

View File

@@ -1,19 +1,3 @@
/*
* 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.function;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -29,7 +13,7 @@ class FunctionTests {
@Test
void test() {
String str = "";
Predicate<String> predicate = PredicateTools.<String>from(Objects::nonNull)
Predicate<String> predicate = Predicates.<String>of(Objects::nonNull)
.and(StringUtils::isNotBlank);
assertFalse(predicate.test(str), "校验应是不通过");
}

View File

@@ -1,73 +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.model;
import static org.junit.jupiter.api.Assertions.assertEquals;
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 lombok.extern.slf4j.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 {
Chinese2ndGenIDCardNumber idCardNumber = Chinese2ndGenIDCardNumber.of("11010520000101111X");
assertEquals("11010520000101111X", idCardNumber.value());
assertEquals(LocalDate.of(2000, 1, 1), idCardNumber.getBirthDate());
assertEquals(Gender.MALE, idCardNumber.getGender());
assertEquals("110105", idCardNumber.getCountyCode());
assertEquals("110105000000", idCardNumber.getFullCountyCode());
assertEquals("1101", idCardNumber.getCityCode());
assertEquals("110100000000", idCardNumber.getFullCityCode());
assertEquals("11", idCardNumber.getProvinceCode());
assertEquals("110000000000", idCardNumber.getFullProvinceCode());
assertEquals("北京", idCardNumber.getProvinceName());
assertThrows(IllegalArgumentException.class,
() -> Chinese2ndGenIDCardNumber.of("1101520000101111"));
assertThrows(IllegalArgumentException.class,
() -> Chinese2ndGenIDCardNumber.of("11010520002101111X"));
try {
Chinese2ndGenIDCardNumber.of("11010520002101111X");
}
catch (IllegalArgumentException e) {
log.error(e.getMessage(), e);
assertTrue(e.getCause() instanceof DateTimeParseException);
}
}
}

View File

@@ -1,108 +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.model;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.DiffBuilder;
import org.apache.commons.lang3.builder.DiffResult;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.annotation.ValueObject;
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
class ValidatableStringRecordTests {
private static final Logger log = LoggerFactory.getLogger(ValidatableStringRecordTests.class);
@Test
void test() {
Username username = Username.of("ZhouXY");
assertNotNull(username);
String usernameStr = username.value();
assertNotNull(usernameStr);
log.info("usernameStr: {}", usernameStr);
List<Username> usernames = Arrays.asList(
Username.of("ZhouXY108"),
Username.of("code_108"),
Username.of("Luquan"),
Username.of("Code108"));
log.info("{}", Collections.max(usernames));
Function<Username, String> compare = o -> o.value().toLowerCase();
log.info("{}", Collections.max(usernames, Comparator.comparing(compare)));
}
}
@AllArgsConstructor
@NoArgsConstructor
@Data
class User {
Username username;
Email email;
static class Diff {
public static DiffResult<User> diff(User left, User right) {
return DiffBuilder.<User>builder()
.setLeft(left)
.setRight(right)
.setStyle(ToStringStyle.JSON_STYLE)
.build()
.append("username", left.username, right.username)
.append("email", left.email, right.email)
.build();
}
}
}
@ValueObject
class Email extends ValidatableStringRecord {
private Email(String value) {
super(value, PatternConsts.EMAIL);
}
@StaticFactoryMethod(Email.class)
public static Email of(String value) {
return new Email(value);
}
}
@ValueObject
class Username extends ValidatableStringRecord {
private Username(String username) {
super(username, PatternConsts.USERNAME);
}
@StaticFactoryMethod(Username.class)
public static Username of(String username) {
return new Username(username);
}
}

Some files were not shown because too many files have changed in this diff Show More