Compare commits

..

26 Commits

Author SHA1 Message Date
1509597032
Merge pull request 'dev' (#12) from dev into master
Reviewed-on: #12
2025-09-21 15:41:49 +08:00
ac92f62967
build(jdk8): 更新构建脚本中的 Kotlin 和 Dokka 插件版本- 在 jdk8/build.gradle.kts 中添加了 Kotlin JVM 插件和 Dokka 插件
- 更新 `build.gradle.kts` 中的 Dokka 版本从2.1.0-Beta 到2.0.0- 调整了 Gradle 任务依赖方式,使用字符串名称替代任务引用
- 升级项目版本号从 4.1.8 到 4.1.9
2025-09-21 15:39:14 +08:00
683aeb2c7f
```
feat(stream): 优化 SuperStream 的 toArray 方法实现- 调整 toArray 方法,支持传入 Class 参数以正确创建泛型数组
- 原无参 toArray 方法改为返回 Object[],避免类型转换异常
- 移除对 ForEach 工具类的依赖
- 提升流处理中数组转换的灵活性与安全性

```

```
feat(collection): 改进 toArray 工具方法支持泛型数组创建

- 新增基于 reified 泛型的 toArray 扩展方法,自动推断数组类型
- 添加带 Class 参数的 toArray 重载方法,用于明确指定元素类型
- 处理空列表时直接返回空数组,避免后续操作异常

```

```
build(gradle): 升级 dokka 插件版本并更新项目依赖- 将 org.jetbrains.dokka 插件升级至 2.1.0-Beta 版本
- 添加 jna5.17.0 依赖用于本地库调用支持
- 引入 mybatis-plus-core 3.0.1 编译依赖
- 更新项目版本号从 4.1.7 至 4.1.8

```
2025-09-21 14:37:22 +08:00
5eed682aa1
feat(utils): 添加 MyBatis-Plus 查询包装器支持
在 build.gradle.kts 中替换 JNA依赖为 mybatis-plus-core,并新增BaseMapperQuery 接口,提供通用的 QueryWrapper 实例创建方法,便于MyBatis-Plus 相关操作的统一封装与复用
2025-09-20 14:22:14 +08:00
578f0a3e89
fix(collection): 抑制类型转换警告并更新文件时间戳修复了 Collection.kt 文件中的类型转换警告问题,通过添加 @Suppress("UNCHECKED_CAST")
注解来抑制编译器警告。同时更新了文件的最后修改时
2025-09-20 14:05:41 +08:00
075dc2346a
feat(utils): 更新工具类并优化集合扩展函数
- 更新多个工具类的最后修改时间和用户信息
- 移除冗余的 main 函数测试代码
- 优化 ByteUtils 中流处理方法的引用- 为 Collection 扩展大量实用函数,包括转换、获取元素和创建集合实例等
- 引入 SuperStream 替代部分自定义流处理逻辑
- 调整 Spring Boot 自动配置包扫描路径- 修复资源读取时的空指针风险,使用 Kotlin 的 elvis 操作符简化代码
2025-09-20 14:01:36 +08:00
23548c0c3d
refactor(Base91): 更新Base91解码表并添加示例
更新了Base91解码表的初始化方式,并添加了一个示例方法来演示编码和解码功能。

feat(Base256): 添加Base256编解码器

新增了Base256类,实现了Base256编解码功能,并在BaseUtils中添加了相应的实例。

refactor(BaseCodec): 增加字符串编解码方法

在BaseCodec接口中增加了对字符串进行Base64编码和解码的方法。refactor(OsPath): 简化路径处理逻辑

移除了OsPath类中不必要的方法实现,简化了路径处理逻辑。

feat(UUID): 支持Base256短字符串

在UUID类中添加了对Base256短字符串的支持,包括编码和解码方法。
2025-09-18 14:43:01 +08:00
4b187f3774
添加 String.join 的文档
添加 List.join 的文档
添加 split 的文档
2025-09-18 09:28:06 +08:00
c90c1d590b
修改 List.join 为<T> 2025-09-18 09:24:43 +08:00
42a3302495
添加 String.join 方法 2025-09-18 09:23:19 +08:00
496c3e6248
更新List.join方法 2025-09-18 09:22:55 +08:00
9002f41c63
Merge pull request 'dev' (#11) from dev into master
Reviewed-on: #11
2025-09-17 21:20:20 +08:00
58806e85f1
refactor(bcrypt): 重构 BCrypt.kt 文件
- 将 BCrypt 对象中的函数转换为顶级函数
- 添加 @JvmName 注解以自定义 JVM 方法名
- 更新相关文件中的导入和引用
- 调整 DateTime 类的 parse 和 format 方法
- 优化 StringUtils 中的 isEmpty 方法
2025-09-17 21:15:39 +08:00
0f5748d55d
feat(utils): 添加 BCrypt 加密工具类
- 新增 BCrypt.kt 文件,提供 BCrypt 加密相关方法
- 更新 build.gradle.kts,调整依赖项和版本
- 更新 gradle.properties,升级版本号至 4.1.1
- 更新 HashUtils.kt,使用新的 BCrypt 工具类
- 添加 NOTICE 文件,记录第三方软件的版权声明
2025-09-17 12:10:53 +08:00
8f8ffc72db
Merge pull request 'feat(build): 重构项目并添加 Maven 发布支持' (#10) from dev into master
Some checks failed
Gitea Actions Build / Build (push) Failing after 2s
Reviewed-on: #10
2025-09-17 11:12:41 +08:00
fcd528a821
feat(build): 重构项目并添加 Maven 发布支持
- 更新 .gitignore 文件,添加 secret.gpg
- 移除 .prettierrc 文件
- 重构 AES 加密工具类,使用新的 Base64 实现
- 新增 Base16、Base64 和 Base91 编解码工具类
- 新增 BaseCodec接口和 BaseUtils 工具类
- 更新 build.gradle.kts 配置,添加 Maven 发布和签名
- 修复 ByteUtils 和 DateTime 工具类中的问题
- 重构 UUID 使用自己的实现 不依赖标准库以及其他的 UUID库
2025-09-17 11:12:10 +08:00
0450e87c13
Merge pull request 'dev' (#9) from dev into master
All checks were successful
Gitea Actions Build / Build (push) Successful in 3m11s
Reviewed-on: #9
2025-09-15 22:33:56 +08:00
541a8a82b4
feat(network): 添加网络地址和端口相关工具类
- 新增 NetworkAddress 类用于表示网络地址,支持 IPv4 和 IPv6
- 新增 NetworkPort 类用于表示端口号
- 新增 NetworkEndpoint 类用于封装网络地址和端口信息
- 优化 AutoConfiguration 类,添加更多系统信息
- 更新 AesUtils、Base64Utils等类的版本信息
- 删除 Minecraft SLF 相关无用代码
- 更新项目版本号至 4.0.7
2025-09-15 22:32:56 +08:00
7c3c13e28c
feat(utils): 更新版本号并优化 UUID 功能
- 将项目版本号从 4.0.5 升级到 4.0.6
- 更新 Main.kt 文件,使用 UUID 替代 DateTime
- 改进 UUID.kt 中的 equals 方法,支持与 JUUID 类型的比较
2025-09-15 18:03:35 +08:00
e4bb9884c1
Merge pull request 'refactor(stream): 重构流处理相关代码' (#8) from dev into master
All checks were successful
Gitea Actions Build / Build (push) Successful in 2m21s
Reviewed-on: #8
2025-09-15 17:27:10 +08:00
cc3156572f
refactor(stream): 重构流处理相关代码
- 移除 InOutSteam、InputStreanWrapper、OutputStreamWrapper 类
- 删除 Getable、GetIdable、GetKeyable、GetNameable 接口- 更新 ByteUtils 和 Collection.kt 中的流处理逻辑
- 新增 InputStreamUtils.kt 文件,提供输入流相关扩展函数
- 更新项目版本号至 4.0.5
2025-09-15 17:26:39 +08:00
0b1aac8ecc
Merge pull request 'refactor(mybatis): 更新类型处理器以支持 JDBC 类型参数为 null' (#7) from dev into master
All checks were successful
Gitea Actions Build / Build (push) Successful in 2m25s
Reviewed-on: #7
2025-09-15 13:55:22 +08:00
9a6c6cd662
refactor(mybatis): 更新类型处理器以支持 JDBC 类型参数为 null
- 修改 DateTimeTypeHandler、MysqlUUIDBinaryTypeHandler 和 UUIDTypeHandler
- 将 setNonNullParameter 方法的 jdbcType 参数变更为可空类型
- 更新方法实现以适应新的可空参数
- 提升代码的健壮性和灵活性,允许在某些情况下不指定 JDBC 类型
2025-09-15 13:55:01 +08:00
3d1f249970
Merge pull request 'dev' (#6) from dev into master
Some checks failed
Gitea Actions Build / Build (push) Failing after 2m14s
Reviewed-on: #6
2025-09-15 12:46:12 +08:00
b329953377
feat(foreach): 增加带有提前终止功能的遍历方法并优化代码结构
- 新增 forEachB 系列函数,支持在遍历过程中提前终止
- 为数组和集合添加专门的 forEach 和 forEachB 函数
- 优化现有 forEach 函数的实现,提高性能
- 添加针对 Map 类型的 forEachMap 和 forEachMapB 函数
- 更新函数注释,明确参数和返回值的含义
2025-09-15 12:45:59 +08:00
f9e96fccd3
feat(foreach): 增加带有提前终止功能的遍历方法并优化代码结构
- 新增 forEachB 系列函数,支持在遍历过程中提前终止
- 为数组和集合添加专门的 forEach 和 forEachB 函数
- 优化现有 forEach 函数的实现,提高性能
- 添加针对 Map 类型的 forEachMap 和 forEachMapB 函数
- 更新函数注释,明确参数和返回值的含义
2025-09-15 12:45:16 +08:00
87 changed files with 6417 additions and 4930 deletions

1
.gitignore vendored
View File

@ -45,3 +45,4 @@ log
node_modules
*lock*
.kotlin
secret.gpg

View File

@ -1,8 +0,0 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"plugins": [
"prettier-plugin-java"
],
"tabWidth": 4,
"useTabs": true
}

25
NOTICE Normal file
View File

@ -0,0 +1,25 @@
mingli-utils
Copyright 2025 mingliqiye
This product includes software developed by third parties.
The original copyright notices and licenses are reproduced below.
------------------------------------------------------------
https://github.com/jeremyh/jBCrypt (org.mindrot:jbcrypt@0.4)
Copyright (c) 2006 Damien Miller <djm@mindrot.org>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
------------------------------------------------------------

View File

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils
* CurrentFile build.gradle.kts
* LastUpdate 2025-09-15 11:20:04
* LastUpdate 2025-09-21 15:36:59
* UpdateUser MingLiPro
*/
@ -26,8 +26,9 @@ import java.time.format.DateTimeFormatter
plugins {
idea
java
id("java-library")
id("maven-publish")
signing
`java-library`
`maven-publish`
kotlin("jvm") version "2.2.20"
id("org.jetbrains.dokka") version "2.0.0"
}
@ -64,23 +65,21 @@ java {
}
dependencies {
annotationProcessor("org.jetbrains:annotations:24.0.0")
annotationProcessor("org.projectlombok:lombok:1.18.38")
implementation("org.slf4j:slf4j-api:2.0.17")
implementation("com.mingliqiye.utils.jna:WinKernel32Api:1.0.1")
// https://github.com/jeremyh/jBCrypt
implementation("org.mindrot:jbcrypt:0.4")
compileOnly("org.springframework.boot:spring-boot-starter:2.7.14")
compileOnly("com.fasterxml.jackson.core:jackson-databind:2.19.2")
compileOnly("com.google.code.gson:gson:2.13.1")
compileOnly("org.mybatis:mybatis:3.5.19")
compileOnly("com.alibaba.fastjson2:fastjson2:2.0.58")
compileOnly("org.projectlombok:lombok:1.18.38")
implementation("org.bouncycastle:bcprov-jdk18on:1.81")
implementation("com.github.f4b6a3:uuid-creator:6.1.0")
implementation("org.mindrot:jbcrypt:0.4")
implementation("org.jetbrains:annotations:24.0.0")
compileOnly("net.java.dev.jna:jna:5.17.0")
//implementation("jakarta.annotation:jakarta.annotation-api:2.1.1")
implementation("org.slf4j:slf4j-api:2.0.17")
implementation("com.mingliqiye.utils.jna:WinKernel32Api:1.0.1")
compileOnly("com.baomidou:mybatis-plus-core:3.0.1")
compileOnly("net.java.dev.jna:jna:5.17.0")
}
@ -97,10 +96,12 @@ tasks.withType<JavaExec>().configureEach {
tasks.withType<org.gradle.jvm.tasks.Jar> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from("LICENSE") { into("META-INF") }
from("NOTICE") { into("META-INF") }
manifest {
attributes(
mapOf(
"Main-Class" to "com.mingliqiye.utils.Main",
"Main-Class" to "com.mingliqiye.utils.main.Main",
"Specification-Title" to ARTIFACTID,
"Specification-Version" to VERSIONS,
"Specification-Vendor" to "minglipro",
@ -131,17 +132,16 @@ repositories {
}
mavenCentral()
}
tasks.register<Jar>("javaDocJar") {
group = "build"
archiveClassifier.set("javadoc")
dependsOn(tasks.dokkaJavadoc)
dependsOn("dokkaJavadoc")
from(buildDir.resolve("dokka/javadoc"))
}
tasks.register<Jar>("kotlinDocJar") {
group = "build"
archiveClassifier.set("kotlindoc")
dependsOn(tasks.dokkaHtml)
dependsOn("dokkaHtml")
from(buildDir.resolve("dokka/html"))
}
publishing {
@ -150,6 +150,10 @@ publishing {
name = "MavenRepositoryRaw"
url = uri("C:/data/git/maven-repository-raw")
}
maven {
name = "OSSRepository"
url = uri("C:/data/git/maven-repository-raw-utils")
}
}
publications {
create<MavenPublication>("mavenJava") {
@ -158,8 +162,34 @@ publishing {
artifact(tasks.named("kotlinDocJar"))
artifactId = ARTIFACTID
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))
pom {
name = "mingli-utils"
url = "https://mingli-utils.mingliqiye.com"
description = "A Java/kotlin Utils"
licenses {
license {
name = "The Apache License, Version 2.0"
url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
id = "minglipro"
name = "mingli"
email = "minglipro@163.com"
}
}
scm {
connection = "scm:git:https://git.mingliqiye.com/minglipro/mingli-utils.git"
developerConnection = "scm:git:https://git.mingliqiye.com:minglipro/mingli-utils.git"
url = "https://git.mingliqiye.com/minglipro/mingli-utils"
}
}
}
}
signing {
sign(publishing.publications)
}
}
tasks.build {
@ -179,7 +209,6 @@ tasks.processResources {
DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.SSSSSSS"
)
)
)
)

View File

@ -16,10 +16,13 @@
# ProjectName mingli-utils
# ModuleName mingli-utils
# CurrentFile gradle.properties
# LastUpdate 2025-09-15 12:01:24
# LastUpdate 2025-09-21 15:38:52
# UpdateUser MingLiPro
#
JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils
VERSIONS=4.0.2
VERSIONS=4.1.9
signing.keyId=B22AA93B
signing.password=
signing.secretKeyRingFile=secret.gpg

View File

@ -16,7 +16,7 @@
# ProjectName mingli-utils
# ModuleName mingli-utils
# CurrentFile gradle-wrapper.properties
# LastUpdate 2025-09-15 12:01:36
# LastUpdate 2025-09-15 22:32:50
# UpdateUser MingLiPro
#
distributionBase=GRADLE_USER_HOME

2
gradlew vendored
View File

@ -18,7 +18,7 @@
# ProjectName mingli-utils
# ModuleName mingli-utils
# CurrentFile gradlew
# LastUpdate 2025-09-15 12:01:36
# LastUpdate 2025-09-15 22:32:50
# UpdateUser MingLiPro
#

View File

@ -16,12 +16,16 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.jdk8
* CurrentFile build.gradle.kts
* LastUpdate 2025-09-14 18:19:04
* LastUpdate 2025-09-21 15:39:12
* UpdateUser MingLiPro
*/
plugins {
id("java-library")
id("maven-publish")
signing
kotlin("jvm") version "2.2.20"
id("org.jetbrains.dokka") version "2.0.0"
}
val GROUPSID = project.properties["GROUPSID"] as String
val VERSIONS = project.properties["VERSIONS"] as String
@ -39,15 +43,44 @@ publishing {
name = "MavenRepositoryRaw"
url = uri("C:/data/git/maven-repository-raw")
}
maven {
name = "OSSRepository"
url = uri("C:/data/git/maven-repository-raw-utils")
}
}
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
artifactId = "$ARTIFACTID-win-jdk8"
groupId = GROUPSID
version = VERSIONS
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))
pom {
name = "mingli-utils-win-jdk8"
url = "https://mingli-utils.mingliqiye.com"
description = "A Java/kotlin Utils"
licenses {
license {
name = "The Apache License, Version 2.0"
url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
id = "minglipro"
name = "mingli"
email = "minglipro@163.com"
}
}
scm {
connection = "scm:git:https://git.mingliqiye.com/minglipro/mingli-utils.git"
developerConnection = "scm:git:https://git.mingliqiye.com:minglipro/mingli-utils.git"
url = "https://git.mingliqiye.com/minglipro/mingli-utils"
}
}
}
}
signing {
sign(publishing.publications)
}
}
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))

23
jdk8/gradle.properties Normal file
View File

@ -0,0 +1,23 @@
#
# Copyright 2025 mingliqiye
#
# 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
#
# 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.
#
# ProjectName mingli-utils
# ModuleName mingli-utils.jdk8
# CurrentFile gradle.properties
# LastUpdate 2025-09-16 12:14:37
# UpdateUser MingLiPro
#
signing.secretKeyRingFile=../secret.gpg

View File

@ -1 +0,0 @@
lombok.addLombokGeneratedAnnotation = false

View File

@ -1,14 +0,0 @@
{
"name": "maven-repository",
"version": "1.0.0",
"scripts": {
"build": "gradle build-jar",
"buildw": "gradlew build-jar",
"format": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue,astro,json,java}\"",
"f": "prettier --write \"**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,vue,astro,json,java}\""
},
"devDependencies": {
"prettier-plugin-java": "^2.7.1",
"prettier": "^3.6.2"
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils
* CurrentFile settings.gradle.kts
* LastUpdate 2025-09-13 02:37:04
* LastUpdate 2025-09-16 12:32:52
* UpdateUser MingLiPro
*/

View File

@ -1,30 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Main.java
* LastUpdate 2025-09-15 11:18:12
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils;
public class Main {
public static void main(String[] args) {
MainKt.main();
}
}

View File

@ -1,249 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile GsonJsonApi.java
* LastUpdate 2025-09-15 11:20:04
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json;
import com.google.gson.*;
import com.mingliqiye.utils.json.converters.JsonConverter;
import com.mingliqiye.utils.json.converters.JsonStringConverter;
import org.jetbrains.annotations.NotNull;
public class GsonJsonApi implements JsonApi {
private Gson gsonUnicode;
private Gson gsonPretty;
private Gson gsonPrettyUnicode;
private Gson gson;
public GsonJsonApi() {
gson = new GsonBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
gsonUnicode = new GsonBuilder()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
gsonPretty = new GsonBuilder()
.setPrettyPrinting()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
gsonPrettyUnicode = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
}
public GsonJsonApi(Gson gson) {
this.gson = gson
.newBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
this.gsonUnicode = gson
.newBuilder()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
this.gsonPretty = gson
.newBuilder()
.setPrettyPrinting()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
this.gsonPrettyUnicode = gson
.newBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create();
}
@Override
public <T> T parse(String json, Class<T> clazz) {
return gson.fromJson(json, clazz);
}
@Override
public <T> T parse(String json, JsonTypeReference<T> type) {
return gson.fromJson(json, type.getType());
}
@Override
public String format(Object object) {
return gson.toJson(object);
}
@Override
public String formatUnicode(Object object) {
return gsonUnicode.toJson(object);
}
@Override
public String formatPretty(Object object) {
return gsonPretty.toJson(object);
}
@Override
public String formatPrettyUnicode(Object object) {
return gsonPrettyUnicode.toJson(object);
}
@Override
public boolean isValidJson(String json) {
try {
JsonElement element = JsonParser.parseString(json);
return true;
} catch (JsonSyntaxException e) {
return false;
} catch (Exception e) {
return false;
}
}
@Override
public String merge(String... jsons) {
JsonObject merged = new JsonObject();
for (String json : jsons) {
if (json == null || json.isEmpty()) {
continue;
}
try {
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
for (String key : obj.keySet()) {
merged.add(key, obj.get(key));
}
} catch (Exception e) {
// 忽略无效的 JSON 字符串
}
}
return gson.toJson(merged);
}
@Override
public String getNodeValue(String json, String path) {
try {
JsonElement element = JsonParser.parseString(json);
String[] paths = path.split("\\.");
JsonElement current = element;
for (String p : paths) {
if (current.isJsonObject()) {
current = current.getAsJsonObject().get(p);
} else {
return null;
}
if (current == null) {
return null;
}
}
return current.isJsonPrimitive()
? current.getAsString()
: current.toString();
} catch (Exception e) {
return null;
}
}
@Override
public String updateNodeValue(String json, String path, Object newValue) {
try {
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
String[] paths = path.split("\\.");
JsonObject current = obj;
// 导航到倒数第二层
for (int i = 0; i < paths.length - 1; i++) {
String p = paths[i];
if (!current.has(p) || !current.get(p).isJsonObject()) {
current.add(p, new JsonObject());
}
current = current.getAsJsonObject(p);
}
// 设置最后一层的值
String lastPath = paths[paths.length - 1];
if (newValue == null) {
current.remove(lastPath);
} else {
JsonElement element = gson.toJsonTree(newValue);
current.add(lastPath, element);
}
return gson.toJson(obj);
} catch (Exception e) {
return json;
}
}
@Override
public <T, D> D convert(T source, Class<D> destinationClass) {
String json = gson.toJson(source);
return gson.fromJson(json, destinationClass);
}
@Override
public <T, D> D convert(T source, JsonTypeReference<D> destinationType) {
String json = gson.toJson(source);
return gson.fromJson(json, destinationType.getType());
}
@Override
public void addJsonConverter(@NotNull JsonConverter<?, ?> c) {
gson = gson
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
gsonUnicode = gsonUnicode
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
gsonPretty = gsonPretty
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
gsonPrettyUnicode = gsonPrettyUnicode
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
}
@Override
public void addJsonStringConverter(@NotNull JsonStringConverter<?> c) {
addJsonConverter(c);
}
}

View File

@ -1,355 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JacksonJsonApi.java
* LastUpdate 2025-09-15 11:16:53
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mingliqiye.utils.json.converters.JsonConverter;
import com.mingliqiye.utils.json.converters.JsonStringConverter;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* 基于Jackson的JSON处理实现类提供JSON字符串解析格式化合并节点操作等功能
*/
public class JacksonJsonApi implements JsonApi {
private final ObjectMapper objectMapper;
/**
* 使用默认的ObjectMapper构造实例
*/
public JacksonJsonApi() {
this.objectMapper = new ObjectMapper();
}
/**
* 使用指定的ObjectMapper构造实例
*
* @param objectMapper 自定义的ObjectMapper实例
*/
public JacksonJsonApi(ObjectMapper objectMapper) {
this.objectMapper = objectMapper.copy();
}
/**
* 将JSON字符串解析为指定类型的对象
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象类型
* @param <T> 泛型参数表示目标对象类型
* @return 解析后的对象
* @throws JsonException 当解析失败时抛出异常
*/
@Override
public <T> T parse(String json, Class<T> clazz) {
try {
return objectMapper.readValue(json, clazz);
} catch (IOException e) {
throw new JsonException("Failed to parse JSON string", e);
}
}
/**
* 将JSON字符串解析为复杂泛型结构的对象如ListMap等
*
* @param json JSON字符串
* @param type 泛型类型引用
* @param <T> 泛型参数表示目标对象类型
* @return 解析后的对象
* @throws JsonException 当解析失败时抛出异常
*/
@Override
public <T> T parse(String json, JsonTypeReference<T> type) {
try {
ObjectReader reader = objectMapper.readerFor(
objectMapper.constructType(type.getType())
);
return reader.readValue(json);
} catch (IOException e) {
throw new JsonException("Failed to parse JSON string", e);
}
}
/**
* 将对象格式化为JSON字符串
*
* @param object 待格式化的对象
* @return 格式化后的JSON字符串
* @throws JsonException 当格式化失败时抛出异常
*/
@Override
public String format(Object object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new JsonException(
"Failed to format object to JSON string",
e
);
}
}
@Override
public String formatUnicode(Object object) {
try {
return objectMapper
.writer()
.with(JsonGenerator.Feature.ESCAPE_NON_ASCII)
.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new JsonException(e);
}
}
/**
* 将对象格式化为美化带缩进的JSON字符串
*
* @param object 待格式化的对象
* @return 美化后的JSON字符串
* @throws JsonException 当格式化失败时抛出异常
*/
@Override
public String formatPretty(Object object) {
try {
return objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new JsonException(
"Failed to format object to pretty JSON string",
e
);
}
}
@Override
public String formatPrettyUnicode(Object object) {
try {
return objectMapper
.writerWithDefaultPrettyPrinter()
.with(JsonGenerator.Feature.ESCAPE_NON_ASCII)
.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new JsonException(
"Failed to format object to pretty JSON string",
e
);
}
}
/**
* 将JSON字符串解析为指定元素类型的List
*
* @param json JSON字符串
* @param elementType List中元素的类型
* @param <T> 泛型参数表示List中元素的类型
* @return 解析后的List对象
*/
@Override
public <T> List<T> parseList(String json, Class<T> elementType) {
return parse(json, JsonTypeUtils.listType(elementType));
}
/**
* 将JSON字符串解析为指定键值类型的Map
*
* @param json JSON字符串
* @param keyType Map中键的类型
* @param valueType Map中值的类型
* @param <K> 泛型参数表示Map中键的类型
* @param <V> 泛型参数表示Map中值的类型
* @return 解析后的Map对象
*/
@Override
public <K, V> Map<K, V> parseMap(
String json,
Class<K> keyType,
Class<V> valueType
) {
return parse(json, JsonTypeUtils.MapType(keyType, valueType));
}
/**
* 判断给定字符串是否是有效的JSON格式
*
* @param json 待验证的字符串
* @return 如果是有效JSON返回true否则返回false
*/
@Override
public boolean isValidJson(String json) {
try {
objectMapper.readTree(json);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 合并多个JSON字符串为一个JSON对象
*
* @param jsons 多个JSON字符串
* @return 合并后的JSON字符串
* @throws JsonException 当合并失败时抛出异常
*/
@Override
public String merge(String... jsons) {
ObjectNode result = objectMapper.createObjectNode();
for (String json : jsons) {
try {
JsonNode node = objectMapper.readTree(json);
if (node.isObject()) {
result.setAll((ObjectNode) node);
}
} catch (IOException e) {
// 忽略无效的JSON字符串
}
}
try {
return objectMapper.writeValueAsString(result);
} catch (JsonProcessingException e) {
throw new JsonException("Failed to merge JSON strings", e);
}
}
/**
* 获取JSON字符串中指定路径的节点值
*
* @param json JSON字符串
* @param path 节点路径使用"."分隔
* @return 节点值的文本表示如果路径不存在则返回null
* @throws JsonException 当获取节点值失败时抛出异常
*/
@Override
public String getNodeValue(String json, String path) {
try {
JsonNode node = objectMapper.readTree(json);
String[] paths = path.split("\\.");
for (String p : paths) {
node = node.get(p);
if (node == null) {
return null;
}
}
return node.asText();
} catch (IOException e) {
throw new JsonException("Failed to get node value", e);
}
}
/**
* 更新JSON字符串中指定路径的节点值
*
* @param json JSON字符串
* @param path 节点路径使用"."分隔
* @param newValue 新的节点值
* @return 更新后的JSON字符串
* @throws JsonException 当更新节点值失败时抛出异常
*/
@Override
public String updateNodeValue(String json, String path, Object newValue) {
try {
JsonNode node = objectMapper.readTree(json);
if (node instanceof ObjectNode) {
ObjectNode objectNode = (ObjectNode) node;
String[] paths = path.split("\\.");
JsonNode current = objectNode;
// 导航到目标节点的父节点
for (int i = 0; i < paths.length - 1; i++) {
current = current.get(paths[i]);
if (current == null || !(current instanceof ObjectNode)) {
return json; // 路径不存在或无效
}
}
// 更新值
if (current instanceof ObjectNode) {
ObjectNode parent = (ObjectNode) current;
if (newValue == null) {
parent.remove(paths[paths.length - 1]);
} else {
parent.set(
paths[paths.length - 1],
objectMapper.valueToTree(newValue)
);
}
}
return objectMapper.writeValueAsString(objectNode);
}
return json;
} catch (IOException e) {
throw new JsonException("Failed to update node value", e);
}
}
/**
* 在不同对象类型之间进行转换
*
* @param source 源对象
* @param destinationClass 目标对象类型
* @param <T> 源对象类型
* @param <D> 目标对象类型
* @return 转换后的对象
*/
@Override
public <T, D> D convert(T source, Class<D> destinationClass) {
return objectMapper.convertValue(source, destinationClass);
}
/**
* 在不同泛型对象类型之间进行转换
*
* @param source 源对象
* @param destinationType 目标对象的泛型类型引用
* @param <T> 源对象类型
* @param <D> 目标对象类型
* @return 转换后的对象
*/
@Override
public <T, D> D convert(T source, JsonTypeReference<D> destinationType) {
return objectMapper.convertValue(
source,
objectMapper.constructType(destinationType.getType())
);
}
@Override
public void addJsonConverter(@NotNull JsonConverter<?, ?> c) {
objectMapper.registerModule(c.getStringConverter().getJacksonJsonStringConverterAdapter().getJacksonModule());
}
@Override
public void addJsonStringConverter(@NotNull JsonStringConverter<?> c) {
addJsonConverter(c);
}
}

View File

@ -1,175 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonTypeReference.java
* LastUpdate 2025-09-09 09:20:05
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Objects;
import lombok.Getter;
/**
* 通用的 JSON 类型引用类用于在运行时保留泛型类型信息
* 适用于所有 JSON JacksonGsonFastjson
*
* @param <T> 引用的泛型类型
*/
@Getter
public abstract class JsonTypeReference<T>
implements Comparable<JsonTypeReference<T>> {
protected final Type type;
/**
* 构造函数通过反射获取泛型类型信息
* 仅供内部匿名子类使用
*/
protected JsonTypeReference() {
Type superClass = getClass().getGenericSuperclass();
// 检查是否为匿名子类防止直接实例化导致无法获取泛型信息
if (superClass instanceof Class) {
throw new IllegalArgumentException(
"必须使用匿名子类方式创建 JsonTypeReference" +
"例如: new JsonTypeReference<List<String>>() {}"
);
}
this.type =
((ParameterizedType) superClass).getActualTypeArguments()[0];
}
/**
* 构造函数直接指定类型
* @param type 具体的类型信息
*/
protected JsonTypeReference(Type type) {
this.type = Objects.requireNonNull(type, "Type cannot be null");
}
/**
* 创建类型引用实例
* @param <T> 目标类型
* @return 类型引用实例
*/
public static <T> JsonTypeReference<T> of() {
return new JsonTypeReference<T>() {};
}
/**
* 根据 Class 创建类型引用
* @param clazz 目标类
* @param <T> 目标类型
* @return 类型引用实例
*/
public static <T> JsonTypeReference<T> of(Class<T> clazz) {
return new JsonTypeReference<T>(clazz) {};
}
/**
* 根据 Type 创建类型引用
* @param type 目标类型
* @param <T> 目标类型
* @return 类型引用实例
*/
public static <T> JsonTypeReference<T> of(Type type) {
return new JsonTypeReference<T>(type) {};
}
/**
* 获取原始类型去掉泛型参数的类型
* @return 原始类型 Class
*/
@SuppressWarnings("unchecked")
public Class<T> getRawType() {
Type rawType = type;
// 如果是参数化类型则提取原始类型部分
if (type instanceof ParameterizedType) {
rawType = ((ParameterizedType) type).getRawType();
}
if (rawType instanceof Class) {
return (Class<T>) rawType;
}
throw new IllegalStateException("无法获取原始类型: " + type);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JsonTypeReference<?> that = (JsonTypeReference<?>) o;
// 对于 ParameterizedType需要更完整的比较
if (
this.type instanceof ParameterizedType &&
that.type instanceof ParameterizedType
) {
ParameterizedType thisParamType = (ParameterizedType) this.type;
ParameterizedType thatParamType = (ParameterizedType) that.type;
return (
Objects.equals(
thisParamType.getRawType(),
thatParamType.getRawType()
) &&
Arrays.equals(
thisParamType.getActualTypeArguments(),
thatParamType.getActualTypeArguments()
) &&
Objects.equals(
thisParamType.getOwnerType(),
thatParamType.getOwnerType()
)
);
}
return Objects.equals(type, that.type);
}
@Override
public int hashCode() {
// 针对 ParameterizedType 进行完整哈希计算
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
return Objects.hash(
paramType.getRawType(),
Arrays.hashCode(paramType.getActualTypeArguments()),
paramType.getOwnerType()
);
}
return Objects.hash(type);
}
@Override
public String toString() {
return "JsonTypeReference{" + type + '}';
}
@Override
public int compareTo(JsonTypeReference<T> o) {
return this.type.toString().compareTo(o.type.toString());
}
}

View File

@ -1,253 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonTypeUtils.java
* LastUpdate 2025-09-09 09:18:08
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* JSON 类型工具类提供类型相关的工具方法
*/
public class JsonTypeUtils {
private JsonTypeUtils() {
// 工具类防止实例化
}
/**
* 检查给定的类型是否是指定类或其子类/实现类
*
* @param type 要检查的类型
* @param expectedClass 期望匹配的类
* @return 如果类型匹配则返回 true否则返回 false
*/
public static boolean isTypeOf(Type type, Class<?> expectedClass) {
if (type instanceof Class) {
return expectedClass.isAssignableFrom((Class<?>) type);
} else if (type instanceof ParameterizedType) {
return isTypeOf(
((ParameterizedType) type).getRawType(),
expectedClass
);
}
return false;
}
/**
* 获取泛型类型的参数类型
*
* @param type 泛型类型
* @param index 参数索引从0开始
* @return 指定位置的泛型参数类型
* @throws IllegalArgumentException 当无法获取指定索引的泛型参数时抛出异常
*/
public static Type getGenericParameter(Type type, int index) {
if (type instanceof ParameterizedType) {
Type[] typeArgs =
((ParameterizedType) type).getActualTypeArguments();
if (index >= 0 && index < typeArgs.length) {
return typeArgs[index];
}
}
throw new IllegalArgumentException(
"无法获取泛型参数: " + type + " at index " + index
);
}
/**
* 获取类型名称支持普通类和泛型类型
*
* @param type 类型对象
* @return 类型名称字符串
*/
public static String getTypeName(Type type) {
if (type instanceof Class) {
return ((Class<?>) type).getSimpleName();
} else if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
Class<?> rawType = (Class<?>) pType.getRawType();
Type[] typeArgs = pType.getActualTypeArguments();
StringBuilder sb = new StringBuilder(rawType.getSimpleName());
sb.append("<");
for (int i = 0; i < typeArgs.length; i++) {
if (i > 0) sb.append(", ");
sb.append(getTypeName(typeArgs[i]));
}
sb.append(">");
return sb.toString();
}
return type.getTypeName();
}
/**
* 创建一个表示数组类型的引用对象
*
* @param componentType 数组元素的类型
* @param <T> 元素类型
* @return 表示数组类型的 JsonTypeReference 对象
*/
public static <T> JsonTypeReference<T[]> arrayType(Class<T> componentType) {
return new JsonTypeReference<T[]>() {
private final Type arrayType = java.lang.reflect.Array.newInstance(
componentType,
0
).getClass();
@Override
public Type getType() {
return new ParameterizedType() {
private final Type[] actualTypeArguments = new Type[] {
componentType,
};
@Override
public Type[] getActualTypeArguments() {
return actualTypeArguments;
}
@Override
public Type getRawType() {
return arrayType;
}
@Override
public Type getOwnerType() {
return null;
}
};
}
};
}
/**
* 创建一个表示 List 类型的引用对象
*
* @param componentType List 中元素的类型
* @param <T> 元素类型
* @return 表示 List 类型的 JsonTypeReference 对象
* @throws IllegalArgumentException 如果 componentType null则抛出异常
*/
public static <T> JsonTypeReference<List<T>> listType(
Class<T> componentType
) {
if (componentType == null) {
throw new IllegalArgumentException("componentType cannot be null");
}
return new JsonTypeReference<List<T>>() {
@Override
public Type getType() {
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[] { componentType };
}
@Override
public Type getRawType() {
return List.class;
}
@Override
public Type getOwnerType() {
return null;
}
};
}
};
}
/**
* 创建一个表示 Map 类型的引用对象
*
* @param keyType Map 键的类型
* @param valueType Map 值的类型
* @param <K> 键类型
* @param <V> 值类型
* @return 表示 Map 类型的 JsonTypeReference 对象
* @throws IllegalArgumentException 如果 keyType valueType null则抛出异常
*/
public static <K, V> JsonTypeReference<Map<K, V>> MapType(
Class<K> keyType,
Class<V> valueType
) {
if (keyType == null) {
throw new IllegalArgumentException("keyType cannot be null");
}
if (valueType == null) {
throw new IllegalArgumentException("valueType cannot be null");
}
return new JsonTypeReference<Map<K, V>>() {
@Override
public Type getType() {
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[] { keyType, valueType };
}
@Override
public Type getRawType() {
return Map.class;
}
@Override
public Type getOwnerType() {
return null;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof ParameterizedType)) return false;
ParameterizedType that = (ParameterizedType) obj;
return (
Objects.equals(getRawType(), that.getRawType()) &&
Arrays.equals(
getActualTypeArguments(),
that.getActualTypeArguments()
) &&
Objects.equals(getOwnerType(), that.getOwnerType())
);
}
@Override
public int hashCode() {
return (
Arrays.hashCode(getActualTypeArguments()) ^
Objects.hashCode(getRawType()) ^
Objects.hashCode(getOwnerType())
);
}
};
}
};
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Description.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.minecraft.slp;
import lombok.Data;
@Data
public class Description {
private String text;
private Extra[] extra;
}

View File

@ -1,34 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Extra.java
* LastUpdate 2025-09-09 08:37:34
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.minecraft.slp;
import lombok.Data;
@Data
public class Extra {
private String text;
private String color;
private Boolean bold;
private Boolean italic;
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MinecraftServerStatus.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.minecraft.slp;
import lombok.Data;
@Data
public class MinecraftServerStatus {
private Description description;
private Players players;
private Version version;
private String favicon;
private boolean enforcesSecureChat;
private boolean previewsChat;
private String jsonData;
}

View File

@ -1,219 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SLP.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.minecraft.slp;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mingliqiye.utils.network.NetworkEndpoint;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Minecraft 服务器列表协议Server List Ping, SLP工具类
* 提供了与 Minecraft 服务器通信以获取其状态信息的功能
*/
public class SLP {
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* int32 值截断为无符号 short2 字节并按大端序写入字节数组
*
* @param value 需要转换的整数int32
* @return 包含两个字节的数组表示无符号 short
*/
public static byte[] toUnsignedShort(int value) {
byte[] array = new byte[2];
ByteBuffer.wrap(array, 0, 2)
.order(ByteOrder.BIG_ENDIAN)
.putShort((short) (value & 0xFFFF));
return array;
}
/**
* 构造 Minecraft 握手包数据
* 握手包用于初始化客户端与服务器之间的连接
*
* @param serverIP 服务器 IP 地址或域名
* @param serverPort 服务器端口号
* @param type 连接类型通常为 1 表示获取状态
* @return 握手包的完整字节数组
* @throws IOException 如果构造过程中发生 IO 错误
*/
public static byte[] getHandshakePack(
String serverIP,
int serverPort,
int type
) throws IOException {
ByteArrayOutputStream pack = new ByteArrayOutputStream();
ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
pack.write(0x00); // 握手包标识符
pack.write(toVarInt(1156)); // 协议版本号示例值
byte[] sip = serverIP.getBytes();
pack.write(toVarInt(sip.length)); // 服务器地址长度
pack.write(sip); // 服务器地址
pack.write(toUnsignedShort(serverPort)); // 服务器端口
pack.write(toVarInt(type)); // 下一阶段类型1 表示状态请求
byteArrayOutputStream.write(toVarInt(pack.size())); // 包长度前缀
byteArrayOutputStream.write(pack.toByteArray());
return byteArrayOutputStream.toByteArray();
}
/**
* 获取状态请求包的固定字节表示
* 此包用于向服务器请求当前状态信息
*
* @return 状态请求包的字节数组
*/
public static byte[] getStatusPack() {
return new byte[] { 0x01, 0x00 };
}
/**
* 从输入流中读取服务器返回的状态 JSON 数据并解析为 MinecraftServerStatus 实体对象
*
* @param inputStream 输入流包含服务器响应的数据
* @return 解析后的 MinecraftServerStatus 对象
* @throws IOException 如果读取过程中发生 IO 错误
*/
public static MinecraftServerStatus getStatusJsonEntity(
DataInputStream inputStream
) throws IOException {
readVarInt(inputStream); // 忽略第一个 VarInt包长度
inputStream.readByte(); // 忽略包标识符
int lengthjson = readVarInt(inputStream); // 读取 JSON 数据长度
byte[] data = new byte[lengthjson];
inputStream.readFully(data); // 读取完整的 JSON 数据
MinecraftServerStatus serverStatus = objectMapper.readValue(
data,
MinecraftServerStatus.class
);
serverStatus.setJsonData(new String(data)); // 设置原始 JSON 字符串
return serverStatus;
}
/**
* 从输入流中读取一个 VarInt 类型的整数最多 5 个字节
*
* @param in 输入流
* @return 解码后的整数值
* @throws IOException 如果读取过程中发生 IO 错误
*/
public static int readVarInt(DataInputStream in) throws IOException {
int value = 0;
int length = 0;
byte currentByte;
do {
currentByte = in.readByte();
value |= (currentByte & 0x7F) << (length * 7);
length += 1;
if (length > 5) {
throw new RuntimeException("VarInt too long");
}
} while ((currentByte & 0x80) != 0);
return value;
}
/**
* 将一个 int32 整数编码为 VarInt 格式的字节数组1 5 个字节
*
* @param value 需要编码的整数
* @return 编码后的 VarInt 字节数组
*/
public static byte[] toVarInt(int value) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
while (true) {
if ((value & 0xFFFFFF80) == 0) {
buffer.write(value); // 最后一个字节
break;
}
buffer.write((value & 0x7F) | 0x80); // 写入带继续位的字节
value >>>= 7; // 右移 7 位继续处理
}
return buffer.toByteArray();
}
/**
* 创建一个新的 Socket 连接到指定的网络端点并设置超时时间
*
* @param networkEndpoint 目标网络端点包括主机和端口
* @return 已连接的 Socket 实例
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static Socket getNewConnect(NetworkEndpoint networkEndpoint)
throws IOException {
Socket socket = new Socket();
socket.setSoTimeout(5000); // 设置读取超时时间为 5
socket.connect(networkEndpoint.toInetSocketAddress()); // 执行连接操作
return socket;
}
/**
* 使用 "host:port" 格式的字符串连接到 Minecraft 服务器并获取其状态信息
*
* @param s 域名或 IP 地址加端口号组成的字符串例如 "127.0.0.1:25565"
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static MinecraftServerStatus getServerStatus(String s)
throws IOException {
return getServerStatus(NetworkEndpoint.of(s));
}
/**
* 使用指定的主机名和端口号连接到 Minecraft 服务器并获取其状态信息
*
* @param s 主机名或 IP 地址
* @param i 端口号
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
*/
public static MinecraftServerStatus getServerStatus(String s, Integer i)
throws IOException {
return getServerStatus(NetworkEndpoint.of(s, i));
}
/**
* 使用 NetworkEndpoint 实例连接到 Minecraft 服务器并获取其状态信息
*
* @param e 网络端点实例包含主机和端口信息
* @return 服务器状态实体对象
* @throws IOException 如果连接失败或发生 IO 错误
* @see NetworkEndpoint
*/
public static MinecraftServerStatus getServerStatus(NetworkEndpoint e)
throws IOException {
Socket socket = getNewConnect(e); // 建立 TCP 连接
OutputStream out = socket.getOutputStream(); // 获取输出流发送数据
DataInputStream in = new DataInputStream(socket.getInputStream()); // 获取输入流接收数据
out.write(getHandshakePack(e.getHost(), e.getPort(), 1)); // 发送握手包
out.write(getStatusPack()); // 发送状态请求包
return getStatusJsonEntity(in); // 读取并解析服务器响应
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Version.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.minecraft.slp;
import lombok.Data;
@Data
public class Version {
private String name;
private int protocol;
}

View File

@ -1,218 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkAddress.java
* LastUpdate 2025-09-14 22:12:16
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtils;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Pattern;
/**
* 网络地址类用于表示一个网络地址IP或域名并提供相关操作
* 支持IPv4和IPv6地址的解析与验证
*
* @author MingLiPro
*/
public class NetworkAddress implements Serializable {
/**
* IPv6标识
*/
public static int IPV6 = 6;
/**
* IPv4标识
*/
public static int IPV4 = 4;
/**
* IPv4地址正则表达式
*/
static String IPV4REG =
"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" +
"(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$";
/**
* 编译后的IPv4地址匹配模式
*/
private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4REG);
/**
* IPv6地址正则表达式
*/
static String IPV6REG =
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" +
"^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" +
"|" +
"^(::)$|" +
"^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" +
"^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" +
"^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$";
/**
* 编译后的IPv6地址匹配模式
*/
private static final Pattern IPV6_PATTERN = Pattern.compile(IPV6REG);
/**
* IP地址类型4 表示 IPv46 表示 IPv6
*/
@Getter
private int IPv;
/**
* IP地址字符串
*/
@Getter
private String ip;
/**
* 域名如果输入的是域名
*/
private String domain;
/**
* 标识是否是域名解析来的IP
*/
private boolean isdom;
/**
* 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理
*
* @param domip 可能是IP地址或域名的字符串
*/
NetworkAddress(String domip) {
try {
// 尝试将输入识别为IP地址
IPv = testIp(domip);
ip = domip;
} catch (NetworkException e) {
try {
// 如果不是有效IP则尝试作为域名解析
String ips = getHostIp(domip);
IPv = testIp(ips);
ip = ips;
isdom = true;
domain = domip;
} catch (UnknownHostException ex) {
throw new NetworkException(ex);
}
}
}
/**
* 静态工厂方法创建 NetworkAddress 实例
*
* @param domip 可能是IP地址或域名的字符串
* @return 新建的 NetworkAddress 实例
*/
public static NetworkAddress of(String domip) {
return new NetworkAddress(domip);
}
/**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
*
* @param inetAddress InetAddress 对象
* @return 新建的 NetworkAddress 实例
*/
public static NetworkAddress of(InetAddress inetAddress) {
return new NetworkAddress(inetAddress.getHostAddress());
}
/**
* 从DNS服务器解析域名获取对应的IP地址
*
* @param domain 域名
* @return 解析出的第一个IP地址
* @throws UnknownHostException 如果域名无法解析
*/
public static String getHostIp(@NotNull String domain)
throws UnknownHostException {
InetAddress[] addresses = InetAddress.getAllByName(domain.trim());
return addresses[0].getHostAddress();
}
/**
* 检测给定字符串是否为有效的IPv4或IPv6地址
*
* @param ip 要检测的IP地址字符串
* @return 4 表示IPv46 表示IPv6
* @throws NetworkException 如果IP格式无效
*/
public static int testIp(String ip) {
if (ip == null) {
throw new NetworkException("IP地址不能为null");
}
String trimmedIp = ip.trim();
// 判断是否匹配IPv4格式
if (IPV4_PATTERN.matcher(trimmedIp).matches()) {
return IPV4;
}
// 判断是否匹配IPv6格式
if (IPV6_PATTERN.matcher(trimmedIp).matches()) {
return IPV6;
}
// 不符合任一格式时抛出异常
throw new NetworkException(
StringUtils.format("[{}] 不是有效的IPv4或IPv6地址", ip)
);
}
/**
* 将当前 NetworkAddress 转换为 InetAddress 对象
*
* @return InetAddress 对象
*/
public InetAddress toInetAddress() {
try {
return InetAddress.getByName(ip != null ? ip : domain);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
/**
* 返回 NetworkAddress 的字符串表示形式
*
* @return 字符串表示
*/
public String toString() {
return isdom
? StringUtils.format(
"NetworkAddress(IP='{}',type='{}'," + "domain='{}')",
ip,
IPv,
domain
)
: StringUtils.format("NetworkAddress(IP='{}',type='{}')", ip, IPv);
}
}

View File

@ -1,164 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkEndpoint.java
* LastUpdate 2025-09-14 22:12:16
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtils;
import lombok.Getter;
import java.io.Serializable;
import java.net.InetSocketAddress;
/**
* IP和端口聚集类用于封装网络地址与端口信息
* 该类提供了与InetSocketAddress之间的相互转换功能
*
* @author MingLiPro
* @see InetSocketAddress
*/
public class NetworkEndpoint implements Serializable {
@Getter
private final NetworkAddress networkAddress;
@Getter
private final NetworkPort networkPort;
/**
* 构造函数使用指定的网络地址和端口创建NetworkEndpoint实例
*
* @param networkAddress 网络地址对象
* @param networkPort 网络端口对象
* @see NetworkAddress
* @see NetworkPort
*/
private NetworkEndpoint(
NetworkAddress networkAddress,
NetworkPort networkPort
) {
this.networkAddress = networkAddress;
this.networkPort = networkPort;
}
/**
* 根据给定的InetSocketAddress对象创建NetworkEndpoint实例
*
* @param address InetSocketAddress对象
* @return 新建的NetworkEndpoint实例
* @see InetSocketAddress
*/
public static NetworkEndpoint of(InetSocketAddress address) {
return new NetworkEndpoint(
new NetworkAddress(address.getHostString()),
new NetworkPort(address.getPort())
);
}
/**
* 根据主机名或IP字符串和端口号创建NetworkEndpoint实例
*
* @param s 主机名或IP地址字符串
* @param i 端口号
* @return 新建的NetworkEndpoint实例
*/
public static NetworkEndpoint of(String s, Integer i) {
NetworkAddress networkAddress = new NetworkAddress(s);
NetworkPort networkPort = new NetworkPort(i);
return new NetworkEndpoint(networkAddress, networkPort);
}
/**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080"
*
* @param s "host:port"格式的字符串
* @return 新建的NetworkEndpoint实例
*/
public static NetworkEndpoint of(String s) {
// 查找最后一个冒号的位置以支持IPv6地址中的冒号
int lastColonIndex = s.lastIndexOf(':');
return of(
s.substring(0, lastColonIndex),
Integer.parseInt(s.substring(lastColonIndex + 1))
);
}
/**
* 将当前NetworkEndpoint转换为InetSocketAddress对象
*
* @return 对应的InetSocketAddress对象
* @see InetSocketAddress
*/
public InetSocketAddress toInetSocketAddress() {
return new InetSocketAddress(
networkAddress.toInetAddress(),
networkPort.getPort()
);
}
/**
* 将当前NetworkEndpoint转换为"host:port"格式的字符串
* 例如"127.0.0.1:25563"
*
* @return 格式化后的字符串
*/
public String toHostPortString() {
return StringUtils.format(
"{}:{}",
networkAddress.getIp(),
networkPort.getPort()
);
}
/**
* 返回NetworkEndpoint的详细字符串表示形式
* 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...)
*
* @return 包含详细信息的字符串
*/
public String toString() {
return StringUtils.format(
"NetworkEndpoint(IP={},Port={},Endpoint={})",
networkAddress.getIp(),
networkPort.getPort(),
toHostPortString()
);
}
/**
* 获取主机名或IP地址字符串
*
* @return 主机名或IP地址
*/
public String getHost() {
return networkAddress.getIp();
}
/**
* 获取端口号
*
* @return 端口号
*/
public Integer getPort() {
return networkPort.getPort();
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkPort.java
* LastUpdate 2025-09-14 22:12:16
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network;
import com.mingliqiye.utils.string.StringUtils;
import lombok.Getter;
import java.io.Serializable;
/**
* 网络端口类
*
* @author MingLiPro
*/
public class NetworkPort implements Serializable {
@Getter
private final int port;
/**
* 构造函数创建一个网络端口对象
*
* @param port 端口号必须在0-65535范围内
*/
public NetworkPort(int port) {
testPort(port);
this.port = port;
}
/**
* 验证端口号是否合法
*
* @param port 待验证的端口号
* @throws NetworkException 当端口号不在合法范围(0-65535)内时抛出异常
*/
public static void testPort(int port) {
// 验证端口号范围是否在0-65535之间
if (!(0 <= port && 65535 >= port)) {
throw new NetworkException(
StringUtils.format("{} 不是正确的端口号", port)
);
}
}
}

View File

@ -1,279 +0,0 @@
package com.mingliqiye.utils.stream;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jetbrains.annotations.NotNull;
/**
* 自定义的输入输出流工具类支持线程安全的数据读写操作<br>
* 谁闲着没事干用本地输入输出<br>
* 实现了 AutoCloseableCloseable Flushable 接口便于资源管理
*
* @author MingLiPro
*/
public class InOutSteam implements AutoCloseable, Closeable, Flushable {
private final Lock lock = new ReentrantLock();
List<Byte> bytes;
/**
* 从内部缓冲区中读取最多 len 个字节到指定的字节数组 b
*
* @param b 目标字节数组用于存储读取的数据
* @param off 起始偏移位置
* @param len 最大读取字节数
* @return 实际读取的字节数如果已到达流末尾则返回 -1
* @throws IndexOutOfBoundsException 如果 off len 参数非法
*/
public int read(byte@NotNull [] b, int off, int len) {
if (bytes == null) return -1;
if (bytes.isEmpty()) {
return 0;
}
if (off < 0 || len < 0 || off + len > b.length) {
throw new IndexOutOfBoundsException();
}
try {
lock.lock();
int bytesRead = Math.min(len, bytes.size());
for (int i = 0; i < bytesRead; i++) {
b[off + i] = bytes.get(i);
}
if (bytesRead > 0) {
bytes.subList(0, bytesRead).clear();
}
return bytesRead;
} finally {
lock.unlock();
}
}
/**
* 读取一个字节的数据
*
* @return 下一个字节数据0~255如果已到达流末尾则返回 -1
*/
public int read() {
if (bytes == null) return -1;
try {
lock.lock();
if (bytes.isEmpty()) {
return -1;
}
return bytes.remove(0);
} finally {
lock.unlock();
}
}
/**
* 从内部缓冲区中读取最多 b.length 个字节到指定的字节数组 b
*
* @param b 目标字节数组用于存储读取的数据
* @return 实际读取的字节数如果已到达流末尾则返回 -1
*/
public int read(byte@NotNull [] b) {
if (bytes == null) return -1;
return read(b, 0, b.length);
}
/**
* 跳过并丢弃此输入流中数据的 n 个字节
*
* @param n 要跳过的字节数
* @return 实际跳过的字节数
*/
public long skip(long n) {
if (bytes == null) return -1;
if (bytes.isEmpty()) {
return 0;
}
try {
lock.lock();
if (n <= 0) {
return 0;
}
long bytesToSkip = Math.min(n, bytes.size());
// 移除跳过的字节
if (bytesToSkip > 0) {
bytes.subList(0, (int) bytesToSkip).clear();
}
return bytesToSkip;
} finally {
lock.unlock();
}
}
/**
* 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取或跳过的估计字节数
*
* @return 可以无阻塞读取的字节数
*/
public int available() {
if (bytes == null) return -1;
try {
lock.lock();
return bytes.size();
} finally {
lock.unlock();
}
}
/**
* 获取与当前对象关联的 InputStream 实例
*
* @return InputStream 实例
*/
public InputStream getInputStream() {
if (inputStream == null) {
inputStream = inputStream();
}
return inputStream;
}
private InputStream inputStream;
private OutputStream outputStream;
/**
* 获取与当前对象关联的 OutputStream 实例
*
* @return OutputStream 实例
*/
public OutputStream getOutputStream() {
if (outputStream == null) {
outputStream = outputStream();
}
return outputStream;
}
/**
* 创建并返回一个包装后的 InputStream 实例
*
* @return 新创建的 InputStream 实例
*/
private InputStream inputStream() {
return new InputStreanWrapper(
new InputStream() {
@Override
public int read() {
return InOutSteam.this.read();
}
@Override
public int read(byte@NotNull [] b, int off, int len) {
return InOutSteam.this.read(b, off, len);
}
@Override
public long skip(long n) {
return InOutSteam.this.skip(n);
}
@Override
public int available() {
return InOutSteam.this.available();
}
}
);
}
/**
* 创建并返回一个 OutputStream 实例
*
* @return 新创建的 OutputStream 实例
*/
private OutputStream outputStream() {
return new OutputStream() {
@Override
public void write(int b) {
InOutSteam.this.write(b);
}
@Override
public void write(byte@NotNull [] b, int off, int len) {
InOutSteam.this.write(b, off, len);
}
};
}
/**
* 构造函数初始化内部字节列表
*/
public InOutSteam() {
bytes = new ArrayList<>();
}
/**
* 将指定的字节写入此输出流
*
* @param b 要写入的字节
*/
public void write(int b) {
try {
lock.lock();
bytes.add((byte) b);
} finally {
lock.unlock();
}
}
/**
* 将指定字节数组中的部分数据写入此输出流
*
* @param b 数据源字节数组
* @param off 起始偏移位置
* @param len 写入的字节数
* @throws IndexOutOfBoundsException 如果 off len 参数非法
*/
public void write(byte@NotNull [] b, int off, int len) {
if (off < 0 || len < 0 || off + len > b.length) {
throw new IndexOutOfBoundsException("Invalid offset or length");
}
try {
lock.lock();
for (int i = off; i < off + len; i++) {
bytes.add(b[i]);
}
} finally {
lock.unlock();
}
}
/**
* 将整个字节数组写入此输出流
*
* @param b 要写入的字节数组
*/
public void write(byte@NotNull [] b) {
write(b, 0, b.length);
}
/**
* 刷新此输出流并强制写出所有缓冲的输出字节
* 当前实现为空方法
*/
@Override
public void flush() {}
/**
* 关闭此流并释放与其相关的所有资源
* 清空并置空内部字节列表
*/
@Override
public void close() {
bytes.clear();
bytes = null;
}
}

View File

@ -1,123 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile InputStreamUtils.java
* LastUpdate 2025-09-15 08:30:57
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.stream;
import com.mingliqiye.utils.collection.Collections;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
/**
* 输入流工具类提供对InputStream的常用操作封装
*/
public class InputStreamUtils {
/**
* 默认缓冲区大小1MB
*/
public static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;
/**
* 将输入流读取到字节数组中<br>
* 请在外部自行关闭输入流对象 避免资源泄露
*
* @param inputStream 输入流对象用于读取数据
* @return 包含输入流所有数据的字节数组
* @throws IOException 当读取输入流或写入输出流时发生IO异常
*/
public static byte[] readToArray(InputStream inputStream)
throws IOException {
// 使用ByteArrayOutputStream来收集输入流中的所有数据
try (val byteArrayOutputStream = new ByteArrayOutputStream()) {
val bytes = new byte[DEFAULT_BUFFER_SIZE];
int length;
// 循环读取输入流数据直到读取完毕
while ((length = inputStream.read(bytes)) != -1) {
byteArrayOutputStream.write(bytes, 0, length);
}
return byteArrayOutputStream.toByteArray();
}
}
/**
* 将输入流读取到Byte列表中<br>
* 请在外部自行关闭输入流对象 避免资源泄露
*
* @param inputStream 输入流对象用于读取数据
* @return 包含输入流所有数据的Byte列表
* @throws IOException 当读取输入流时发生IO异常
*/
public static @NotNull List<Byte> readToList(InputStream inputStream)
throws IOException {
return Collections.newArrayLists(readToArray(inputStream));
}
/**
* 将输入流读取为字符串<br>
* 请在外部自行关闭输入流对象 避免资源泄露
*
* @param inputStream 输入流对象用于读取数据
* @return 输入流对应的字符串内容
* @throws IOException 当读取输入流时发生IO异常
*/
public static String readToString(InputStream inputStream)
throws IOException {
return new String(readToArray(inputStream));
}
/**
* 将输入流的数据传输到输出流中<br>
* 请在外部自行关闭输入流输出流对象 避免资源泄露
*
* @param inputStream 源输入流用于读取数据
* @param outputStream 目标输出流用于写入数据
* @return 传输的总字节数
* @throws IOException 当读取或写入流时发生IO异常
*/
public static long transferTo(
InputStream inputStream,
OutputStream outputStream
) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream can not be null");
}
if (outputStream == null) {
throw new IllegalArgumentException("outputStream can not be null");
}
val bytes = new byte[DEFAULT_BUFFER_SIZE];
int length;
long readAll = 0L;
// 循环读取并写入数据直到输入流读取完毕
while ((length = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, length);
readAll += length;
}
outputStream.flush();
return readAll;
}
}

View File

@ -1,120 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile InputStreanWrapper.java
* LastUpdate 2025-09-14 22:12:16
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.stream;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public class InputStreanWrapper extends InputStream implements AutoCloseable {
@Getter
private final InputStream inputStream;
public InputStreanWrapper(InputStream inputStream) {
this.inputStream = inputStream;
}
private static InputStreanWrapper of(InputStream inputStream) {
return new InputStreanWrapper(inputStream);
}
@Override
public int available() throws IOException {
return inputStream.available();
}
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public int read(byte@NotNull [] b) throws IOException {
return inputStream.read(b);
}
@Override
public int read(byte@NotNull [] b, int off, int len) throws IOException {
return inputStream.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return inputStream.skip(n);
}
@Override
public void mark(int readlimit) {
inputStream.mark(readlimit);
}
@Override
public void reset() throws IOException {
inputStream.reset();
}
@Override
public boolean markSupported() {
return inputStream.markSupported();
}
@Override
public void close() {
try {
inputStream.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 输入流转换为输出流 <br>
* jdk8 兼容实现 jdk9+ <br>
* 请使用 InputStream.transferTo()
*
* @param outputStream 输出流
* @return 转换的字节数
* @throws IOException IO错误
*/
public long transferToOutputStream(OutputStream outputStream)
throws IOException {
return InputStreamUtils.transferTo(inputStream, outputStream);
}
public byte[] readToArray() throws IOException {
return InputStreamUtils.readToArray(inputStream);
}
public List<Byte> readToList() throws IOException {
return InputStreamUtils.readToList(inputStream);
}
public String readToString() throws IOException {
return InputStreamUtils.readToString(inputStream);
}
}

View File

@ -1,81 +0,0 @@
package com.mingliqiye.utils.stream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
public class OutputStreamWrapper extends OutputStream implements AutoCloseable {
@Getter
private final OutputStream outputStream;
private final ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
public OutputStreamWrapper(OutputStream outputStream) {
this.outputStream = outputStream;
}
public static OutputStreamWrapper of(OutputStream outputStream) {
return new OutputStreamWrapper(outputStream);
}
@Override
public void write(int b) throws IOException {
byteArrayOutputStream.write(b);
}
@Override
public void write(byte@NotNull [] b) throws IOException {
byteArrayOutputStream.write(b);
}
public void write(List<Byte> b) throws IOException {
write(b, 0, b.size());
}
@Override
public void write(byte@NotNull [] b, int off, int len) throws IOException {
byteArrayOutputStream.write(b, off, len);
}
public void write(List<Byte> b, int off, int len) throws IOException {
byte[] bytes = new byte[b.size()];
for (int i = 0; i < b.size(); i++) {
bytes[i] = b.get(i);
}
byteArrayOutputStream.write(bytes, off, len);
}
@Override
public void flush() throws IOException {
outputStream.write(byteArrayOutputStream.toByteArray());
byteArrayOutputStream.reset();
}
public int getBufferCachedSize() {
return byteArrayOutputStream.size();
}
public byte[] getBufferCachedBytes() {
return byteArrayOutputStream.toByteArray();
}
@Override
public void close() {
try {
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public long transferFromOutputStream(InputStream inputStream)
throws IOException {
return InputStreamUtils.transferTo(inputStream, outputStream);
}
}

View File

@ -15,18 +15,20 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile PlayerSample.java
* LastUpdate 2025-09-09 08:37:33
* CurrentFile StreamEmptyException.java
* LastUpdate 2025-09-20 13:24:07
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.minecraft.slp;
package com.mingliqiye.utils.stream;
import lombok.Data;
public class StreamEmptyException extends java.lang.RuntimeException {
@Data
public class PlayerSample {
public StreamEmptyException(String message) {
super(message);
}
private String name;
private String id;
public StreamEmptyException(String message, Throwable cause) {
super(message, cause);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
package com.mingliqiye.utils.stream.interfaces;
public interface GetIdable<T> extends Getable<T> {
T getId();
default T get() {
return getId();
}
}

View File

@ -1,9 +0,0 @@
package com.mingliqiye.utils.stream.interfaces;
public interface GetKeyable<T> {
T getKey();
default T get() {
return getKey();
}
}

View File

@ -1,9 +0,0 @@
package com.mingliqiye.utils.stream.interfaces;
public interface GetNameable<T> extends Getable<T> {
T getName();
default T get() {
return getName();
}
}

View File

@ -1,5 +0,0 @@
package com.mingliqiye.utils.stream.interfaces;
public interface Getable<T> {
T get();
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AesUtils.kt
* LastUpdate 2025-09-14 18:43:04
* LastUpdate 2025-09-19 21:35:53
* UpdateUser MingLiPro
*/
@ -25,8 +25,7 @@
package com.mingliqiye.utils.aes
import com.mingliqiye.utils.base64.decode
import com.mingliqiye.utils.base64.encode
import com.mingliqiye.utils.base.BASE64
import java.nio.charset.StandardCharsets
import java.security.GeneralSecurityException
import java.security.MessageDigest
@ -72,8 +71,8 @@ fun encrypt(sSrc: String, sKey: String?): String? {
val encrypted = cipher.doFinal(
sSrc.toByteArray(StandardCharsets.UTF_8)
)
return encode(
"${encode(iv)}:${encode(encrypted)}".toByteArray()
return BASE64.encode(
"${BASE64.encode(iv)}:${BASE64.encode(encrypted)}".toByteArray()
)
}
@ -86,13 +85,13 @@ fun encrypt(sSrc: String, sKey: String?): String? {
fun decrypt(sSrc: String, sKey: String): String? {
try {
// 分割IV和加密数据
val sSrcs = String(decode(sSrc))
val sSrcs = String(BASE64.decode(sSrc))
val parts: Array<String?> = sSrcs.split(":".toRegex(), limit = 2).toTypedArray()
if (parts.size != 2) {
return null
}
val iv = decode(parts[0]!!)
val encryptedData = decode(parts[1]!!)
val iv = BASE64.decode(parts[0]!!)
val encryptedData = BASE64.decode(parts[1]!!)
if (iv.size != GCM_IV_LENGTH) {
return null
}
@ -122,3 +121,4 @@ private fun createSecretKey(sKey: String): SecretKeySpec {
val digest = md.digest(key)
return SecretKeySpec(digest, ALGORITHM)
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Base16.kt
* LastUpdate 2025-09-17 10:56:07
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.base
/**
* Base16编解码器实现类
* 提供字节数组与十六进制字符串之间的相互转换功能
*/
class Base16 : BaseCodec {
/**
* 将字节数组编码为十六进制字符串
* @param bytes 待编码的字节数组
* @return 编码后的十六进制字符串每个字节对应两位十六进制字符
*/
override fun encode(bytes: ByteArray): String {
// 将每个字节转换为两位十六进制字符串并拼接
return bytes.joinToString("") {
it.toInt().and(0xff).toString(16).padStart(2, '0')
}
}
/**
* 将十六进制字符串解码为字节数组
* @param string 待解码的十六进制字符串
* @return 解码后的字节数组
*/
override fun decode(string: String): ByteArray {
// 按每两个字符分组,转换为字节
return string.chunked(2).map {
it.toInt(16).toByte()
}.toByteArray()
}
}

View File

@ -0,0 +1,313 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Base256.kt
* LastUpdate 2025-09-20 14:01:29
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.base
/**
* Base256 字符集 256
*
* 256个字符 要字符集的下面复制
*
* !#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~§±×÷
*
*/
class Base256 : BaseCodec {
companion object {
val code = arrayOf(
'!',
'#',
'$',
'%',
'&',
'(',
')',
'*',
'+',
',',
'-',
'.',
'/',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
':',
';',
'<',
'=',
'>',
'?',
'@',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
'[',
']',
'^',
'_',
'`',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'{',
'|',
'}',
'~',
'§',
'±',
'×',
'÷',
'←',
'↑',
'→',
'↓',
'⇒',
'⇔',
'∀',
'∃',
'∅',
'∆',
'∇',
'∈',
'∉',
'∋',
'∌',
'∏',
'∑',
'',
'∓',
'',
'',
'∘',
'∙',
'√',
'∛',
'∜',
'∞',
'∟',
'∠',
'',
'∥',
'∧',
'',
'∩',
'',
'∫',
'∬',
'∭',
'∮',
'∯',
'∰',
'∱',
'∲',
'∳',
'∴',
'∵',
'',
'∷',
'≈',
'≠',
'≡',
'≤',
'≥',
'≦',
'≧',
'≪',
'≫',
'≺',
'≻',
'⊂',
'⊃',
'⊆',
'⊇',
'⊈',
'⊉',
'⊊',
'⊋',
'⊕',
'⊖',
'⊗',
'⊘',
'⊙',
'⊚',
'⊛',
'⊜',
'⊝',
'⊞',
'⊟',
'⊠',
'⊡',
'⊢',
'⊣',
'',
'⊥',
'⊦',
'⊧',
'⊨',
'⊩',
'⊪',
'⊫',
'⊬',
'⊭',
'⊮',
'⊯',
'⋀',
'',
'⋂',
'',
'⋄',
'⋅',
'⋆',
'⋇',
'⋈',
'⋉',
'⋊',
'⋋',
'⋌',
'⋍',
'⋎',
'⋏',
'⋐',
'⋑',
'⋒',
'⋓',
'⋔',
'⋕',
'⋖',
'⋗',
'⋘',
'⋙',
'⋚',
'⋛',
'⋜',
'⋝',
'⋞',
'⋟',
'⋠',
'⋡',
'⋢',
'⋣',
'⋤',
'⋥',
'⋦',
'⋧',
'⋨',
'⋩',
'▁',
'▂',
'▃',
'▄',
'▅',
'▆',
'▇',
'█',
'▉',
'▊',
'▋',
'▌',
'▍',
'▎',
'▏',
'▐',
'░',
'▒',
'▓',
'▔',
'▕',
'▖',
'▗',
'▘',
'▙'
)
val codeMap = code.mapIndexed { index, c -> c to index }.toMap()
}
override fun encode(bytes: ByteArray): String {
val result = CharArray(bytes.size)
for (i in bytes.indices) {
val unsignedByte = bytes[i].toInt() and 0xFF
result[i] = code[unsignedByte]
}
return String(result)
}
override fun decode(string: String): ByteArray {
val result = ByteArray(string.length)
for (i in string.indices) {
result[i] = codeMap[string[i]]!!.toByte()
}
return result
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Base64.kt
* LastUpdate 2025-09-17 10:56:32
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.base
/*
* Base64编解码工具类
* 提供Base64编码和解码功能的实现
*/
class Base64 : BaseCodec {
/*
* Base64编码器实例
* 用于执行字节数组到Base64字符串的编码操作
*/
val BASE_64_ENCODER: java.util.Base64.Encoder = java.util.Base64.getEncoder()
/*
* Base64解码器实例
* 用于执行Base64字符串到字节数组的解码操作
*/
val BASE_64_DECODER: java.util.Base64.Decoder = java.util.Base64.getDecoder()
/*
* 将字节数组编码为Base64字符串
*
* @param bytes 待编码的字节数组
* @return 编码后的Base64字符串
*/
override fun encode(bytes: ByteArray): String {
return BASE_64_ENCODER.encodeToString(bytes)
}
/*
* 将Base64字符串解码为字节数组
*
* @param string 待解码的Base64字符串
* @return 解码后的字节数组
*/
override fun decode(string: String): ByteArray {
return BASE_64_DECODER.decode(string)
}
}

View File

@ -0,0 +1,404 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Base91.kt
* LastUpdate 2025-09-19 20:08:46
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.base
/**
* Base91 编解码工具类用于将字节数组编码为 Base91 字符串或将 Base91 字符串解码为原始字节数组
*
* Base91 是一种高效的二进制到文本的编码方式相较于 Base64它使用更少的字符来表示相同的数据
*/
class Base91 : BaseCodec {
companion object {
/**
* Base91 编码表 91 个可打印 ASCII 字符
*/
val ENCODING_TABLE: CharArray = charArrayOf(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$',
'%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=',
'>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"'
)
/**
* Base91 解码表大小为 256用于快速查找字符对应的数值
* 初始化时将所有元素设为 -1表示无效字符然后根据 ENCODING_TABLE 填充有效字符的索引
*/
val DECODING_TABLE: Array<Int> = arrayOf(
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
62,
90,
63,
64,
65,
66,
-1,
67,
68,
69,
70,
71,
-1,
72,
73,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
74,
75,
76,
77,
78,
79,
80,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
81,
-1,
82,
83,
84,
85,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
86,
87,
88,
89,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1
)
}
/**
* 将字节数组编码为 Base91 字符串
*
* @param bytes 待编码的字节数组
* @return 编码后的 Base91 字符串
*/
override fun encode(bytes: ByteArray): String {
if (bytes.isEmpty()) return ""
val sb = StringBuilder()
var ebq = 0 // 编码缓冲区,用于暂存待处理的位数据
var en = 0 // 当前缓冲区中的有效位数
for (b in bytes) {
// 将当前字节加入缓冲区
ebq = ebq or ((b.toInt() and 0xFF) shl en)
en += 8
// 每当缓冲区中有超过 13 位的数据时,尝试进行编码
if (en > 13) {
var ev = ebq and 0x1FFF // 取出低 13 位作为候选值
if (ev > 88) {
// 如果候选值大于 88则使用 13 位编码
ebq = ebq shr 13
en -= 13
} else {
// 否则使用 14 位编码
ev = ebq and 0x3FFF
ebq = ebq shr 14
en -= 14
}
// 将两个字符追加到结果中
sb.append(ENCODING_TABLE[ev % 91])
sb.append(ENCODING_TABLE[ev / 91])
}
}
// 处理剩余未编码的数据
if (en > 0) {
sb.append(ENCODING_TABLE[ebq % 91])
if (en > 7 || ebq > 90) {
sb.append(ENCODING_TABLE[ebq / 91])
}
}
return sb.toString()
}
/**
* Base91 字符串解码为原始字节数组
*
* @param string 待解码的 Base91 字符串
* @return 解码后的字节数组
*/
override fun decode(string: String): ByteArray {
if (string.isEmpty()) return ByteArray(0)
var dbq = 0 // 解码缓冲区,用于暂存待处理的位数据
var dn = 0 // 当前缓冲区中的有效位数
var dv = -1 // 当前读取到的 Base91 值
val buffer = ByteArray(string.length * 13 / 8) // 预分配输出缓冲区
var index = 0 // 输出缓冲区写入位置
for (c in string.toCharArray()) {
// 忽略不在编码表中的字符
if (DECODING_TABLE[c.code] == -1) continue
if (dv == -1) {
// 第一次读取字符,保存为 dv
dv = DECODING_TABLE[c.code]
} else {
// 第二次读取字符,组合成完整的 Base91 值
dv += DECODING_TABLE[c.code] * 91
dbq = dbq or (dv shl dn)
// 根据值大小判断是 13 位还是 14 位编码
dn += if ((dv and 0x1FFF) > 88) 13 else 14
// 将缓冲区中完整的字节写入输出数组
do {
buffer[index++] = (dbq and 0xFF).toByte()
dbq = dbq shr 8
dn -= 8
} while (dn > 7)
dv = -1 // 重置 dv准备下一轮读取
}
}
// 处理最后剩余的一个字符(如果存在)
if (dv != -1) {
buffer[index++] = ((dbq or (dv shl dn)) and 0xFF).toByte()
}
// 返回实际使用的部分
return buffer.copyOf(index)
}
}

View File

@ -0,0 +1,173 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseCodec.kt
* LastUpdate 2025-09-18 14:07:35
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.base
import java.io.File
import java.io.IOException
import java.nio.file.Path
interface BaseCodec {
/**
* 将字节数组编码为Base64字符串
*
* @param bytes 需要编码的字节数组
* @return 编码后的Base64字符串
*/
fun encode(bytes: ByteArray): String
/**
* 将Base64字符串解码为字节数组
*
* @param string 需要解码的Base64字符串
* @return 解码后的字节数组
*/
fun decode(string: String): ByteArray
/**
* 将文件内容编码为Base64字符串
*
* @param file 需要编码的文件
* @return 文件内容的Base64编码字符串
* @throws IOException 当文件读取失败时抛出
*/
@Throws(IOException::class)
fun encode(file: File): String {
return encode(file.readBytes())
}
/**
* 将Base64字符串解码并写入文件
*
* @param file 目标文件
* @param string 需要解码的Base64字符串
* @throws IOException 当文件写入失败时抛出
*/
@Throws(IOException::class)
fun decode(file: File, string: String) {
file.writeBytes(decode(string))
}
/**
* 安全地将文件内容编码为Base64字符串出现异常时返回null
*
* @param file 需要编码的文件
* @return 文件内容的Base64编码字符串失败时返回null
*/
fun encodeSafe(file: File): String? {
return try {
encode(file)
} catch (_: Exception) {
null
}
}
/**
* 安全地将Base64字符串解码并写入文件返回操作是否成功
*
* @param file 目标文件
* @param string 需要解码的Base64字符串
* @return 操作成功返回true失败返回false
*/
fun decodeSafe(file: File, string: String): Boolean {
return try {
decode(file, string)
true
} catch (_: Exception) {
false
}
}
/**
* 将路径对应的文件内容编码为Base64字符串
*
* @param path 需要编码的文件路径
* @return 文件内容的Base64编码字符串
* @throws IOException 当文件读取失败时抛出
*/
@Throws(IOException::class)
fun encode(path: Path): String {
return encode(path.toFile().readBytes())
}
/**
* 将Base64字符串解码并写入路径指定的文件
*
* @param path 目标文件路径
* @param string 需要解码的Base64字符串
* @throws IOException 当文件写入失败时抛出
*/
@Throws(IOException::class)
fun decode(path: Path, string: String) {
path.toFile().writeBytes(decode(string))
}
/**
* 安全地将路径对应的文件内容编码为Base64字符串出现异常时返回null
*
* @param path 需要编码的文件路径
* @return 文件内容的Base64编码字符串失败时返回null
*/
fun encodeSafe(path: Path): String? {
return try {
encode(path)
} catch (_: Exception) {
null
}
}
/**
* 安全地将Base64字符串解码并写入路径指定的文件返回操作是否成功
*
* @param path 目标文件路径
* @param string 需要解码的Base64字符串
* @return 操作成功返回true失败返回false
*/
fun decodeSafe(path: Path, string: String): Boolean {
return try {
decode(path, string)
true
} catch (_: Exception) {
false
}
}
/**
* 将字符串编码为Base64字符串
*
* @param string 需要编码的字符串
* @return 编码后的Base64字符串
*/
fun encode(string: String): String {
return encode(string.toByteArray())
}
/**
* 将Base64字符串解码为字符串
*
* @param string 需要解码的Base64字符串
* @return 解码后的字符串
*/
fun decodetoString(string: String): String {
return decode(string).toString(Charsets.UTF_8)
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BaseUtils.kt
* LastUpdate 2025-09-19 20:18:09
* UpdateUser MingLiPro
*/
@file:JvmName("BaseUtils")
package com.mingliqiye.utils.base
/**
* Base64编解码器实例
* 使用懒加载方式初始化Base64编解码器对象
* 保证线程安全且只在首次访问时创建实例
*/
val BASE64: BaseCodec by lazy {
Base64()
}
/**
* Base91编解码器实例
* 使用懒加载方式初始化Base91编解码器对象
* 保证线程安全且只在首次访问时创建实例
*/
val BASE91: BaseCodec by lazy {
Base91()
}
/**
* Base91编解码器实例
* 使用懒加载方式初始化Base91编解码器对象
* 保证线程安全且只在首次访问时创建实例
*/
val BASE16: BaseCodec by lazy {
Base16()
}
/**
* Base256编解码器实例
* 使用懒加载方式初始化Base256编解码器对象
* 保证线程安全且只在首次访问时创建实例
*/
val BASE256: BaseCodec by lazy {
Base256()
}

View File

@ -1,160 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Base64Utils.kt
* LastUpdate 2025-09-14 18:44:22
* UpdateUser MingLiPro
*/
@file:JvmName("Base64Utils")
package com.mingliqiye.utils.base64
import java.io.File
import java.io.IOException
import java.nio.file.Path
import java.util.*
val BASE_64_ENCODER: Base64.Encoder = Base64.getEncoder()
val BASE_64_DECODER: Base64.Decoder = Base64.getDecoder()
/**
* 将字节数组编码为Base64字符串
*
* @param bytes 需要编码的字节数组
* @return 编码后的Base64字符串
*/
fun encode(bytes: ByteArray): String {
return BASE_64_ENCODER.encodeToString(bytes)
}
/**
* 将Base64字符串解码为字节数组
*
* @param string 需要解码的Base64字符串
* @return 解码后的字节数组
*/
fun decode(string: String): ByteArray {
return BASE_64_DECODER.decode(string)
}
/**
* 将文件内容编码为Base64字符串
*
* @param file 需要编码的文件
* @return 文件内容的Base64编码字符串
* @throws IOException 当文件读取失败时抛出
*/
@Throws(IOException::class)
fun encode(file: File): String {
return encode(file.readBytes())
}
/**
* 将Base64字符串解码并写入文件
*
* @param file 目标文件
* @param string 需要解码的Base64字符串
* @throws IOException 当文件写入失败时抛出
*/
@Throws(IOException::class)
fun decode(file: File, string: String) {
file.writeBytes(decode(string))
}
/**
* 安全地将文件内容编码为Base64字符串出现异常时返回null
*
* @param file 需要编码的文件
* @return 文件内容的Base64编码字符串失败时返回null
*/
fun encodeSafe(file: File): String? {
return try {
encode(file)
} catch (_: Exception) {
null
}
}
/**
* 安全地将Base64字符串解码并写入文件返回操作是否成功
*
* @param file 目标文件
* @param string 需要解码的Base64字符串
* @return 操作成功返回true失败返回false
*/
fun decodeSafe(file: File, string: String): Boolean {
return try {
decode(file, string)
true
} catch (_: Exception) {
false
}
}
/**
* 将路径对应的文件内容编码为Base64字符串
*
* @param path 需要编码的文件路径
* @return 文件内容的Base64编码字符串
* @throws IOException 当文件读取失败时抛出
*/
@Throws(IOException::class)
fun encode(path: Path): String {
return encode(path.toFile().readBytes())
}
/**
* 将Base64字符串解码并写入路径指定的文件
*
* @param path 目标文件路径
* @param string 需要解码的Base64字符串
* @throws IOException 当文件写入失败时抛出
*/
@Throws(IOException::class)
fun decode(path: Path, string: String) {
path.toFile().writeBytes(decode(string))
}
/**
* 安全地将路径对应的文件内容编码为Base64字符串出现异常时返回null
*
* @param path 需要编码的文件路径
* @return 文件内容的Base64编码字符串失败时返回null
*/
fun encodeSafe(path: Path): String? {
return try {
encode(path)
} catch (_: Exception) {
null
}
}
/**
* 安全地将Base64字符串解码并写入路径指定的文件返回操作是否成功
*
* @param path 目标文件路径
* @param string 需要解码的Base64字符串
* @return 操作成功返回true失败返回false
*/
fun decodeSafe(path: Path, string: String): Boolean {
return try {
decode(path, string)
true
} catch (_: Exception) {
false
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile BCrypt.kt
* LastUpdate 2025-09-19 20:17:41
* UpdateUser MingLiPro
*/
@file:JvmName("BCrypt")
package com.mingliqiye.utils.bcrypt
import java.security.SecureRandom
import org.mindrot.jbcrypt.BCrypt as JBCrypt
fun hashpw(string: String): String {
return hashpw(string, gensalt())
}
fun hashpw(string: String, salt: String = gensalt()): String {
return JBCrypt.hashpw(string, salt)
}
fun checkpw(string: String, bcrypted: String): Boolean {
return JBCrypt.checkpw(string, bcrypted)
}
fun gensalt(): String {
return JBCrypt.gensalt()
}
fun gensalt(long: Int): String {
return JBCrypt.gensalt(long)
}
fun gensalt(long: Int, secureRandom: SecureRandom): String {
return JBCrypt.gensalt(long, secureRandom)
}

View File

@ -1,241 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Factory.kt
* LastUpdate 2025-09-14 19:09:28
* UpdateUser MingLiPro
*/
@file:JvmName("Factory")
package com.mingliqiye.utils.bean
import com.mingliqiye.utils.bean.annotation.ComponentBean
import com.mingliqiye.utils.bean.annotation.InjectBean
import java.io.File
import java.net.URL
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
/**
* 存储所有已注册的Bean实例键为Bean名称值为Bean实例
*/
private val BEANS: ConcurrentHashMap<String, Any> = ConcurrentHashMap()
/**
* 存储按类型查找的Bean实例键为Bean的Class对象值为Bean实例
*/
private val TYPE_BEANS: ConcurrentHashMap<KClass<*>, Any> = ConcurrentHashMap()
/**
* 自动扫描指定类所在包下的所有类并注册为Bean
*
* @param c 指定的类用于获取其所在的包
* @throws IllegalArgumentException 如果传入的类为null或位于默认包中
*/
fun autoScan(c: Class<*>?) {
if (c == null) {
throw IllegalArgumentException("Class cannot be null")
}
val pkg = c.`package`
if (pkg == null) {
throw IllegalArgumentException("Class is in the default package")
}
scan(pkg.name)
}
/**
* 扫描指定包路径下的所有类文件并注册其中带有@ComponentBean注解的类为Bean
*
* @param basePackage 要扫描的基础包名
* @throws RuntimeException 如果在扫描过程中发生异常
*/
fun scan(basePackage: String) {
try {
val path = basePackage.replace('.', '/')
val classLoader = Thread.currentThread().contextClassLoader
val resources: Enumeration<URL> = classLoader.getResources(path)
while (resources.hasMoreElements()) {
val resource = resources.nextElement()
val file = File(resource.toURI())
scanDirectory(file, basePackage)
}
injectDependencies()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
/**
* 递归扫描目录中的所有类文件并注册符合条件的类为Bean
*
* @param directory 当前要扫描的目录
* @param packageName 当前目录对应的包名
* @throws Exception 如果在扫描或类加载过程中发生异常
*/
private fun scanDirectory(directory: File, packageName: String) {
val files = directory.listFiles() ?: return
for (file in files) {
if (file.isDirectory) {
scanDirectory(file, "$packageName.${file.name}")
} else if (file.name.endsWith(".class")) {
val className = packageName + '.' + file.name.replace(".class", "")
registerComponent(Class.forName(className))
}
}
}
/**
* 注册一个带有@ComponentBean注解的类为Bean实例
*
* @param clazz 要注册的类
* @throws Exception 如果在实例化类或处理注解时发生异常
*/
private fun registerComponent(clazz: Class<*>) {
if (clazz.isAnnotationPresent(ComponentBean::class.java)) {
val component = clazz.getAnnotation(ComponentBean::class.java)
val name = component.value.ifEmpty { clazz.name }
val instance = clazz.getDeclaredConstructor().newInstance()
BEANS[name] = instance
val kClass = clazz.kotlin
TYPE_BEANS[kClass] = instance
for (interfaceClass in clazz.interfaces) {
TYPE_BEANS.putIfAbsent(interfaceClass.kotlin, instance)
}
}
}
/**
* 对所有已注册的Bean进行依赖注入处理
*
* @throws Exception 如果在注入过程中发生异常
*/
private fun injectDependencies() {
for (bean in BEANS.values) {
for (field in bean.javaClass.declaredFields) {
if (field.isAnnotationPresent(InjectBean::class.java)) {
val inject = field.getAnnotation(InjectBean::class.java)
val dependency = findDependency(field.type, inject.value)
if (dependency == null) {
throw IllegalStateException(
"No suitable dependency found for field " + field.name + " in class " + bean.javaClass.name
)
}
field.isAccessible = true
field.set(bean, dependency)
}
}
}
}
/**
* 根据类型和名称查找对应的依赖实例
*
* @param type 依赖的类型
* @param name 依赖的名称可为空
* @return 找到的依赖实例未找到则返回null
*/
private fun findDependency(type: Class<*>, name: String): Any? {
if (name.isNotEmpty()) {
return BEANS[name]
}
val dependency = TYPE_BEANS[type.kotlin]
if (dependency != null) {
return dependency
}
for (interfaceType in TYPE_BEANS.keys) {
if (type.isAssignableFrom(interfaceType.java)) {
return TYPE_BEANS[interfaceType]
}
}
return null
}
/**
* 将一个对象添加到Bean容器中使用其类名作为键
*
* @param obj 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常
*/
fun add(obj: Any) {
val clazz = obj.javaClass
val name = clazz.name
BEANS[name] = obj
TYPE_BEANS[clazz.kotlin] = obj
try {
injectDependencies()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
/**
* 将一个对象以指定名称添加到Bean容器中
*
* @param name Bean的名称
* @param obj 要添加的对象
* @throws RuntimeException 如果在注入依赖时发生异常
*/
fun add(name: String, obj: Any) {
BEANS[name] = obj
TYPE_BEANS[obj.javaClass.kotlin] = obj
try {
injectDependencies()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
/**
* 根据类型获取对应的Bean实例
*
* @param objclass Bean的类型
* @param T Bean的泛型类型
* @return 对应类型的Bean实例未找到则返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T : Any> get(objclass: KClass<T>): T? {
return TYPE_BEANS[objclass] as? T
}
/**
* 根据名称和类型获取对应的Bean实例
*
* @param name Bean的名称
* @param objclass Bean的类型
* @param T Bean的泛型类型
* @return 对应名称和类型的Bean实例未找到则返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T : Any> get(name: String, objclass: KClass<T>): T? {
return BEANS[name] as? T
}
/**
* 根据名称获取对应的Bean实例
*
* @param name Bean的名称
* @return 对应名称的Bean实例未找到则返回null
*/
fun get(name: String): Any? {
return BEANS[name]
}

View File

@ -1,43 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ComponentBean.kt
* LastUpdate 2025-09-14 18:48:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.bean.annotation
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.CLASS
import kotlin.annotation.AnnotationTarget.FIELD
/**
* 组件bean注解
* @author MingLiPro
*/
@Retention(RUNTIME)
@Target(CLASS)
annotation class ComponentBean(val value: String = "")
/**
* 注入bean注解
* @author MingLiPro
*/
@Retention(RUNTIME)
@Target(FIELD)
annotation class InjectBean(val value: String = "")

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ByteUtils.kt
* LastUpdate 2025-09-15 00:07:22
* LastUpdate 2025-09-20 11:49:05
* UpdateUser MingLiPro
*/
@file:JvmName("ByteUtils")
@ -41,8 +41,20 @@ const val ESC_RESERVED: Byte = 0x06
* @return 包含每个字节对应十六进制字符串的列表
*/
fun ByteArray.getByteArrayString(): MutableList<String> {
return this.toList().stream()
.map { a -> String.format("0X%02X", a!!.toInt() and 0xFF) }
.collect(SuperStream.Collectors.toList())
return this.toList().stream().map { a -> String.format("0X%02X", a!!.toInt() and 0xFF) }
.collect(SuperStream.toList()) as MutableList<String>
}
fun Char.hexDigitToValue(): Int {
return when (this) {
in '0'..'9' -> this - '0'
in 'A'..'F' -> this - 'A' + 10
in 'a'..'f' -> this - 'a' + 10
else -> throw NumberFormatException("Invalid hex character: $this")
}
}
private fun hexStringToByteArray(string: String): ByteArray {
return string.hexToByteArray()
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile CloneUtils.kt
* LastUpdate 2025-09-15 09:30:37
* LastUpdate 2025-09-20 14:01:29
* UpdateUser MingLiPro
*/
@file:JvmName("CloneUtils")
@ -38,8 +38,7 @@ inline fun <reified T> T.deepJsonClone(jsonApi: JsonApi): T {
} catch (e: Exception) {
throw JsonException(
"Failed to deep clone object using JSON",
e
"Failed to deep clone object using JSON", e
)
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Collection.kt
* LastUpdate 2025-09-15 09:30:37
* LastUpdate 2025-09-21 14:36:57
* UpdateUser MingLiPro
*/
@ -26,33 +26,72 @@ package com.mingliqiye.utils.collection
import com.mingliqiye.utils.stream.SuperStream
import java.util.*
import java.util.stream.Collectors
/**
* 将当前集合转换为数组
*
* @param T 集合元素类型
* @return 转换后的数组
*/
inline fun <reified T> Collection<T>.toArray(): Array<T> {
return arrayOf(*this.toTypedArray())
}
/**
* 将当前集合转换为 Map其中键为集合元素本身值由给定函数生成
*
* @param T 集合元素类型
* @param V 映射值的类型
* @param v 用于生成映射值的函数
* @return 转换后的 Map
*/
@Suppress("UNCHECKED_CAST")
inline fun <reified T, V> Collection<T>.toMap(noinline v: (T) -> V): Map<T, V> {
return this.stream().collect(
SuperStream.Collectors.toMap(
SuperStream.Collectors::getThis, v
SuperStream.toMap(
v
)
)
) as Map<T, V>
}
/**
* 将当前集合转换为 Map其中键和值分别由给定函数生成
*
* @param T 集合元素类型
* @param V 映射值的类型
* @param K 映射键的类型
* @param k 用于生成映射键的函数
* @param v 用于生成映射值的函数
* @return 转换后的 Map
*/
inline fun <reified T, V, K> Collection<T>.toMap(noinline k: (T) -> K, noinline v: (T) -> V): Map<K, V> {
return this.stream().collect(
SuperStream.Collectors.toMap(
Collectors.toMap(
k, v
)
)
}
/**
* 将数组转换为 Set
*
* @param T 数组元素类型
* @param array 输入数组
* @return 转换后的 Set
*/
fun <T> toSet(array: Array<T>): Set<T> {
return array.toSet()
}
/**
* 获取集合中的第一个元素如果集合为空则返回 null
*
* @param T 集合元素类型
* @return 第一个元素或 null
*/
inline fun <reified T> Collection<T>.getFirst(): T? {
if (this.isEmpty()) {
return null
@ -63,6 +102,12 @@ inline fun <reified T> Collection<T>.getFirst(): T? {
return this.iterator().next()
}
/**
* 获取数组中的第一个元素如果数组为空则返回 null
*
* @param T 数组元素类型
* @return 第一个元素或 null
*/
inline fun <reified T> Array<T>.getFirst(): T? {
if (this.isEmpty()) {
return null
@ -70,6 +115,12 @@ inline fun <reified T> Array<T>.getFirst(): T? {
return this.first()
}
/**
* 获取集合中的最后一个元素如果集合为空则返回 null
*
* @param T 集合元素类型
* @return 最后一个元素或 null
*/
inline fun <reified T> Collection<T>.getLast(): T? {
if (this.isEmpty()) {
return null
@ -84,6 +135,12 @@ inline fun <reified T> Collection<T>.getLast(): T? {
return lastElement
}
/**
* 获取数组中的最后一个元素如果数组为空则返回 null
*
* @param T 数组元素类型
* @return 最后一个元素或 null
*/
inline fun <reified T> Array<T>.getLast(): T? {
if (this.isEmpty()) {
return null
@ -92,6 +149,14 @@ inline fun <reified T> Array<T>.getLast(): T? {
}
/**
* 根据索引获取集合中的元素若索引越界则返回默认值
*
* @param T 集合元素类型
* @param index 索引位置
* @param defaultValue 默认返回值
* @return 指定索引处的元素或默认值
*/
inline fun <reified T> Collection<T>.getOrDefault(
index: Int, defaultValue: T
): T {
@ -111,54 +176,135 @@ inline fun <reified T> Collection<T>.getOrDefault(
return defaultValue
}
/**
* 创建一个新的 ArrayList 实例
*
* @param T 元素类型
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(): ArrayList<T> {
return ArrayList()
}
/**
* 创建一个指定初始容量的新 ArrayList 实例
*
* @param T 元素类型
* @param size 初始容量大小
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(size: Int): ArrayList<T> {
return ArrayList()
}
/**
* 使用可变参数创建一个新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(vararg elements: T): ArrayList<T> {
return newArrayList(elements.asList())
}
/**
* 将当前集合转换为新的 ArrayList 实例
*
* @param T 元素类型
* @return 新创建的 ArrayList
*/
fun <T> Collection<T>.newArrayLists(): ArrayList<T> {
return newArrayList(this)
}
/**
* 将数组转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入数组
* @return 新创建的 ArrayList
*/
fun <T> newArrayLists(elements: Array<T>): ArrayList<T> {
return newArrayList(elements.asList())
}
/**
* 将集合转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(elements: Collection<T>): ArrayList<T> {
return ArrayList(elements.toList())
}
/**
* Iterable 转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(elements: Iterable<T>): ArrayList<T> {
return newArrayList(elements.toList())
}
/**
* Sequence 转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(elements: Sequence<T>): ArrayList<T> {
return newArrayList(elements.toList())
}
/**
* 创建一个新的 LinkedList 实例
*
* @param T 元素类型
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(): LinkedList<T> {
return LinkedList()
}
/**
* 使用可变参数创建一个新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(vararg elements: T): LinkedList<T> {
val list = newLinkedList<T>()
list.addAll(elements.asList())
return list
}
/**
* 将集合转换为新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(elements: Collection<T>): LinkedList<T> {
val list = newLinkedList<T>()
list.addAll(elements)
return list
}
/**
* Iterable 转换为新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(elements: Iterable<T>): LinkedList<T> {
val list = newLinkedList<T>()
for (element in elements) {
@ -167,26 +313,60 @@ fun <T> newLinkedList(elements: Iterable<T>): LinkedList<T> {
return list
}
/**
* Sequence 转换为新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(elements: Sequence<T>): LinkedList<T> {
return newLinkedList(elements.toList())
}
/**
* 创建一个新的 Vector 实例
*
* @param T 元素类型
* @return 新创建的 Vector
*/
fun <T> newVector(): Vector<T> {
return Vector()
}
/**
* 使用可变参数创建一个新的 Vector 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 Vector
*/
fun <T> newVector(vararg elements: T): Vector<T> {
val vector = newVector<T>()
vector.addAll(elements.asList())
return vector
}
/**
* 将集合转换为新的 Vector 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 Vector
*/
fun <T> newVector(elements: Collection<T>): Vector<T> {
val vector = newVector<T>()
vector.addAll(elements)
return vector
}
/**
* Iterable 转换为新的 Vector 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 Vector
*/
fun <T> newVector(elements: Iterable<T>): Vector<T> {
val vector = newVector<T>()
for (element in elements) {
@ -195,31 +375,72 @@ fun <T> newVector(elements: Iterable<T>): Vector<T> {
return vector
}
/**
* Sequence 转换为新的 Vector 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 Vector
*/
fun <T> newVector(elements: Sequence<T>): Vector<T> {
return newVector(elements.toList())
}
/**
* 创建一个新的 HashSet 实例
*
* @param T 元素类型
* @return 新创建的 HashSet
*/
fun <T> newHashSet(): HashSet<T> {
return HashSet()
}
/**
* 创建一个指定初始容量的新 HashSet 实例
*
* @param T 元素类型
* @param size 初始容量大小
* @return 新创建的 HashSet
*/
fun <T> newHashSet(size: Int): HashSet<T> {
return HashSet(size)
}
/**
* 使用可变参数创建一个新的 HashSet 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 HashSet
*/
fun <T> newHashSet(vararg elements: T): HashSet<T> {
val set = newHashSet<T>()
set.addAll(elements.asList())
return set
}
/**
* 将集合转换为新的 HashSet 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 HashSet
*/
fun <T> newHashSet(elements: Collection<T>): HashSet<T> {
val set = newHashSet<T>()
set.addAll(elements)
return set
}
/**
* Iterable 转换为新的 HashSet 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 HashSet
*/
fun <T> newHashSet(elements: Iterable<T>): HashSet<T> {
val set = newHashSet<T>()
for (element in elements) {
@ -228,30 +449,71 @@ fun <T> newHashSet(elements: Iterable<T>): HashSet<T> {
return set
}
/**
* Sequence 转换为新的 HashSet 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 HashSet
*/
fun <T> newHashSet(elements: Sequence<T>): HashSet<T> {
return newHashSet(elements.toSet())
}
/**
* 创建一个新的 LinkedHashSet 实例
*
* @param T 元素类型
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(): LinkedHashSet<T> {
return LinkedHashSet()
}
/**
* 创建一个指定初始容量的新 LinkedHashSet 实例
*
* @param T 元素类型
* @param size 初始容量大小
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(size: Int): LinkedHashSet<T> {
return LinkedHashSet(size)
}
/**
* 使用可变参数创建一个新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(vararg elements: T): LinkedHashSet<T> {
val set = newLinkedHashSet<T>()
set.addAll(elements.asList())
return set
}
/**
* 将集合转换为新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(elements: Collection<T>): LinkedHashSet<T> {
val set = newLinkedHashSet<T>()
set.addAll(elements)
return set
}
/**
* Iterable 转换为新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(elements: Iterable<T>): LinkedHashSet<T> {
val set = newLinkedHashSet<T>()
for (element in elements) {
@ -260,26 +522,60 @@ fun <T> newLinkedHashSet(elements: Iterable<T>): LinkedHashSet<T> {
return set
}
/**
* Sequence 转换为新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(elements: Sequence<T>): LinkedHashSet<T> {
return newLinkedHashSet(elements.toSet())
}
/**
* 创建一个新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(): TreeSet<T> {
return TreeSet()
}
/**
* 使用可变参数创建一个新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 可变参数列表
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(vararg elements: T): TreeSet<T> {
val set = newTreeSet<T>()
set.addAll(elements.asList())
return set
}
/**
* 将集合转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 输入集合
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(elements: Collection<T>): TreeSet<T> {
val set = newTreeSet<T>()
set.addAll(elements)
return set
}
/**
* Iterable 转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 输入 Iterable
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(elements: Iterable<T>): TreeSet<T> {
val set = newTreeSet<T>()
for (element in elements) {
@ -288,65 +584,160 @@ fun <T : Comparable<T>> newTreeSet(elements: Iterable<T>): TreeSet<T> {
return set
}
/**
* Sequence 转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 输入 Sequence
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(elements: Sequence<T>): TreeSet<T> {
return newTreeSet(elements.toSet())
}
/**
* 将字节数组转换为 ArrayList
*
* @param elements 输入字节数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: ByteArray): ArrayList<Byte> {
return ArrayList(elements.toList())
}
/**
* 将短整型数组转换为 ArrayList
*
* @param elements 输入短整型数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: ShortArray): ArrayList<Short> {
return ArrayList(elements.toList())
}
/**
* 将整型数组转换为 ArrayList
*
* @param elements 输入整型数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: IntArray): ArrayList<Int> {
return ArrayList(elements.toList())
}
/**
* 将长整型数组转换为 ArrayList
*
* @param elements 输入长整型数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: LongArray): ArrayList<Long> {
return ArrayList(elements.toList())
}
/**
* 将浮点数组转换为 ArrayList
*
* @param elements 输入浮点数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: FloatArray): ArrayList<Float> {
return ArrayList(elements.toList())
}
/**
* 将双精度浮点数组转换为 ArrayList
*
* @param elements 输入双精度浮点数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: DoubleArray): ArrayList<Double> {
return ArrayList(elements.toList())
}
/**
* 将布尔数组转换为 ArrayList
*
* @param elements 输入布尔数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: BooleanArray): ArrayList<Boolean> {
return ArrayList(elements.toList())
}
/**
* 将字符数组转换为 ArrayList
*
* @param elements 输入字符数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: CharArray): ArrayList<Char> {
return ArrayList(elements.toList())
}
/**
* 创建一个新的 CopyOnWriteArrayList 实例
*
* @param T 元素类型
* @return 新创建的 CopyOnWriteArrayList
*/
fun <T> newCopyOnWriteArrayList(): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList()
}
/**
* 使用可变参数创建一个新的 CopyOnWriteArrayList 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 CopyOnWriteArrayList
*/
fun <T> newCopyOnWriteArrayList(vararg elements: T): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList(elements.asList())
}
/**
* 将集合转换为新的 CopyOnWriteArrayList 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 CopyOnWriteArrayList
*/
fun <T> newCopyOnWriteArrayList(elements: Collection<T>): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList(elements)
}
/**
* 创建一个新的 Stack 实例
*
* @param T 元素类型
* @return 新创建的 Stack
*/
fun <T> newStack(): Stack<T> {
return Stack()
}
/**
* 使用可变参数创建一个新的 Stack 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 Stack
*/
fun <T> newStack(vararg elements: T): Stack<T> {
val stack = newStack<T>()
stack.addAll(elements.asList())
return stack
}
/**
* 将集合转换为新的 Stack 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 Stack
*/
fun <T> newStack(elements: Collection<T>): Stack<T> {
val stack = newStack<T>()
stack.addAll(elements)
@ -354,22 +745,53 @@ fun <T> newStack(elements: Collection<T>): Stack<T> {
}
/**
* 创建一个新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>): TreeSet<T> {
return TreeSet(comparator)
}
/**
* 使用可变参数创建一个新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 可变参数列表
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, vararg elements: T): TreeSet<T> {
val set = newTreeSet(comparator)
set.addAll(elements.asList())
return set
}
/**
* 将集合转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 输入集合
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, elements: Collection<T>): TreeSet<T> {
val set = newTreeSet(comparator)
set.addAll(elements)
return set
}
/**
* Iterable 转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 输入 Iterable
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, elements: Iterable<T>): TreeSet<T> {
val set = newTreeSet(comparator)
for (element in elements) {
@ -378,32 +800,71 @@ fun <T> newTreeSet(comparator: Comparator<T>, elements: Iterable<T>): TreeSet<T>
return set
}
/**
* Sequence 转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 输入 Sequence
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, elements: Sequence<T>): TreeSet<T> {
return newTreeSet(comparator, elements.toSet())
}
/**
* 将当前集合转换为新的 CopyOnWriteArrayList 实例
*
* @param T 元素类型
* @return 新创建的 CopyOnWriteArrayList
*/
fun <T> Collection<T>.newCopyOnWriteArrayLists(): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList(this)
}
/**
* 将当前集合转换为新的 Stack 实例
*
* @param T 元素类型
* @return 新创建的 Stack
*/
fun <T> Collection<T>.newStacks(): Stack<T> {
val stack = Stack<T>()
stack.addAll(this)
return stack
}
/**
* 将当前集合转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @return 新创建的 TreeSet
*/
fun <T> Collection<T>.newTreeSets(): TreeSet<T> where T : Comparable<T> {
val set = TreeSet<T>()
set.addAll(this)
return set
}
/**
* 将当前集合转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @return 新创建的 TreeSet
*/
fun <T> Collection<T>.newTreeSets(comparator: Comparator<T>): TreeSet<T> {
val set = TreeSet(comparator)
set.addAll(this)
return set
}
/**
* Byte 类型的 List 转换为字节数组
*
* @param list 输入的 Byte 列表
* @return 转换后的字节数组
*/
fun toArray(list: List<Byte>): ByteArray {
val arr = ByteArray(list.size)
for (i in list.indices) {
@ -413,6 +874,12 @@ fun toArray(list: List<Byte>): ByteArray {
}
/**
* Short 类型的 List 转换为短整型数组
*
* @param list 输入的 Short 列表
* @return 转换后的短整型数组
*/
fun toArray(list: List<Short>): ShortArray {
val arr = ShortArray(list.size)
for (i in list.indices) {
@ -421,6 +888,12 @@ fun toArray(list: List<Short>): ShortArray {
return arr
}
/**
* Int 类型的 List 转换为整型数组
*
* @param list 输入的 Int 列表
* @return 转换后的整型数组
*/
fun toArray(list: List<Int>): IntArray {
val arr = IntArray(list.size)
for (i in list.indices) {
@ -429,6 +902,12 @@ fun toArray(list: List<Int>): IntArray {
return arr
}
/**
* Long 类型的 List 转换为长整型数组
*
* @param list 输入的 Long 列表
* @return 转换后的长整型数组
*/
fun toArray(list: List<Long>): LongArray {
val arr = LongArray(list.size)
for (i in list.indices) {
@ -437,6 +916,12 @@ fun toArray(list: List<Long>): LongArray {
return arr
}
/**
* Float 类型的 List 转换为浮点数组
*
* @param list 输入的 Float 列表
* @return 转换后的浮点数组
*/
fun toArray(list: List<Float>): FloatArray {
val arr = FloatArray(list.size)
for (i in list.indices) {
@ -445,6 +930,12 @@ fun toArray(list: List<Float>): FloatArray {
return arr
}
/**
* Double 类型的 List 转换为双精度浮点数组
*
* @param list 输入的 Double 列表
* @return 转换后的双精度浮点数组
*/
fun toArray(list: List<Double>): DoubleArray {
val arr = DoubleArray(list.size)
for (i in list.indices) {
@ -453,6 +944,12 @@ fun toArray(list: List<Double>): DoubleArray {
return arr
}
/**
* Boolean 类型的 List 转换为布尔数组
*
* @param list 输入的 Boolean 列表
* @return 转换后的布尔数组
*/
fun toArray(list: List<Boolean>): BooleanArray {
val arr = BooleanArray(list.size)
for (i in list.indices) {
@ -461,6 +958,12 @@ fun toArray(list: List<Boolean>): BooleanArray {
return arr
}
/**
* Char 类型的 List 转换为字符数组
*
* @param list 输入的 Char 列表
* @return 转换后的字符数组
*/
fun toArray(list: List<Char>): CharArray {
val arr = CharArray(list.size)
for (i in list.indices) {
@ -468,3 +971,23 @@ fun toArray(list: List<Char>): CharArray {
}
return arr
}
/**
* 将任意类型的 List 转换为数组
*
* @param T 元素类型
* @param list 输入的 List
* @return 转换后的数组
*/
inline fun <reified T> toArray(list: List<T>): Array<T> {
if (list.isEmpty())
return arrayOf<T>()
return SuperStream.of<T>(list)
.toArray(T::class.java)
}
fun <T> toArray(list: List<T>, clazz: Class<T>): Array<T> {
return SuperStream.of<T>(list)
.toArray(clazz)
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile IsChanged.kt
* LastUpdate 2025-09-15 09:30:37
* LastUpdate 2025-09-19 20:17:07
* UpdateUser MingLiPro
*/

View File

@ -0,0 +1,273 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ConfigurationProp.kt
* LastUpdate 2025-09-19 11:30:04
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.configuration
/**
* 配置属性注解用于标记配置类中的字段支持通过命令行参数进行初始化
*
* @param name 配置项的名称默认为空字符串表示使用字段名作为配置项名称
* @param description 配置项的描述信息默认为空字符串
* @param showHelper 是否显示帮助信息默认为 true
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(
AnnotationTarget.TYPE_PARAMETER,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.FIELD, // 添加字段支持
AnnotationTarget.PROPERTY // 添加属性支持
)
annotation class ConfigurationProp(val name: String = "", val description: String = "", val showHelper: Boolean = true)
/**
* 根据字段名生成对应的 setter 方法名
*
* @param fieldName 字段名
* @return 对应的 setter 方法名
*/
private fun getSetterName(fieldName: String): String {
return "set" + fieldName.take(1).uppercase() + fieldName.substring(1)
}
/**
* 根据字段名生成对应的 getter 方法名
*
* @param fieldName 字段名
* @return 对应的 getter 方法名
*/
private fun getGetterName(fieldName: String): String {
return "get" + fieldName.take(1).uppercase() + fieldName.substring(1)
}
/**
* 配置初始化器用于解析命令行参数并填充配置对象
*/
open class ConfigurationProps {
companion object {
/**
* 初始化配置类实例并根据命令行参数填充其字段
*
* @param clazz 配置类的 Class 对象
* @param args 命令行参数数组
* @return 初始化后的配置类实例
*/
@JvmStatic
fun <T : ConfigurationProps> init(clazz: Class<T>, args: Array<String>): T {
val mapsArgs = parseArguments(args)
val instance = clazz.getDeclaredConstructor().newInstance()
processFields(clazz, instance, mapsArgs)
return instance
}
/**
* 解析命令行参数将其转换为键值对映射
*
* 支持以下格式
* - `--key=value` `-k=value`键值对形式
* - `--key value` `-k value`键和值分开的形式
* - `--flag` `-f`布尔标志形式默认值为 "true"
*
* @param args 命令行参数数组
* @return 解析后的键值对映射
*/
private fun parseArguments(args: Array<String>): Map<String, List<String>> {
val mapsArgs = mutableMapOf<String, MutableList<String>>()
var i = 0
while (i < args.size) {
val arg = args[i]
when {
arg.startsWith("--") -> {
// 处理 --key=value 格式
if (arg.contains("=")) {
val (key, value) = arg.substring(2).split("=", limit = 2)
mapsArgs.getOrPut(key) { mutableListOf() }.add(value)
}
// 处理 --key value 格式
else if (i + 1 < args.size && !args[i + 1].startsWith("-")) {
val key = arg.substring(2)
val value = args[i + 1]
mapsArgs.getOrPut(key) { mutableListOf() }.add(value)
i++ // 跳过下一个参数
}
// 处理 --flag 格式的布尔标志
else {
val key = arg.substring(2)
mapsArgs.getOrPut(key) { mutableListOf() }.add("true")
}
}
arg.startsWith("-") -> {
// 处理 -k=value 格式
if (arg.contains("=")) {
val (key, value) = arg.substring(1).split("=", limit = 2)
mapsArgs.getOrPut(key) { mutableListOf() }.add(value)
}
// 处理 -k value 格式
else if (i + 1 < args.size && !args[i + 1].startsWith("-")) {
val key = arg.substring(1)
val value = args[i + 1]
mapsArgs.getOrPut(key) { mutableListOf() }.add(value)
i++
}
// 处理 -f 格式的布尔标志
else {
val key = arg.substring(1)
mapsArgs.getOrPut(key) { mutableListOf() }.add("true")
}
}
}
i++
}
return mapsArgs
}
/**
* 处理配置类中的字段根据解析出的参数设置字段值
*
* @param clazz 配置类的 Class 对象
* @param instance 配置类的实例
* @param mapsArgs 解析后的命令行参数映射
*/
private fun <T : ConfigurationProps> processFields(
clazz: Class<T>,
instance: T,
mapsArgs: Map<String, List<String>>
) {
val fields = clazz.declaredFields
for (field in fields) {
val configurationProp = field.getAnnotation(ConfigurationProp::class.java)
if (configurationProp != null) {
val fieldName = configurationProp.name.ifEmpty { field.name }
val values = mapsArgs[fieldName]
if (values != null) {
try {
val setter = clazz.getDeclaredMethod(
getSetterName(field.name),
field.type
)
val value = convertValue(field.type, values, configurationProp)
setter.invoke(instance, value)
} catch (e: Exception) {
println("Warning: Failed to set field ${field.name}: ${e.message}")
}
}
}
}
}
/**
* 将字符串值转换为目标类型
*
* @param type 目标类型
* @param values 字符串值列表
* @param annotation 配置属性注解
* @return 转换后的值
*/
private fun convertValue(
type: Class<*>,
values: List<String>,
annotation: ConfigurationProp
): Any? {
val lastValue = values.lastOrNull() ?: return null
return when (type) {
String::class.java -> lastValue
Integer::class.java, Int::class.java -> try {
lastValue.toInt()
} catch (e: NumberFormatException) {
println("Warning: Invalid integer value '$lastValue'")
null
}
Long::class.java, java.lang.Long::class.java -> try {
lastValue.toLong()
} catch (e: NumberFormatException) {
println("Warning: Invalid long value '$lastValue'")
null
}
Double::class.java, java.lang.Double::class.java -> try {
lastValue.toDouble()
} catch (e: NumberFormatException) {
println("Warning: Invalid double value '$lastValue'")
null
}
Boolean::class.java, java.lang.Boolean::class.java -> when (lastValue.lowercase()) {
"true", "1", "yes", "on" -> true
"false", "0", "no", "off" -> false
else -> {
println("Warning: Invalid boolean value '$lastValue'")
null
}
}
List::class.java -> values
else -> {
println("Warning: Unsupported type ${type.simpleName}")
null
}
}
}
}
fun printHelp() {
val fields = this::class.java.declaredFields
val help = StringBuilder()
for (field in fields) {
val configurationProp = field.getAnnotation(ConfigurationProp::class.java)
if (configurationProp != null && configurationProp.showHelper) {
val fieldName = configurationProp.name.ifEmpty { field.name }
help.append("$fieldName -> 类型: ${field.type.simpleName}")
if (configurationProp.description.isNotEmpty()) {
help.append(" 描述: ${configurationProp.description}")
}
help.append("\n")
}
}
println(help)
}
val fields: Map<String, Any?>
get() {
val fields = this::class.java.declaredFields
val fieldValues = mutableMapOf<String, Any?>()
for (field in fields) {
field.isAccessible = true
val fieldName = field.name
val fieldValue = field.get(this)
fieldValues[fieldName] = fieldValue
}
return fieldValues
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DtoUtils.kt
* LastUpdate 2025-09-19 13:38:56
* UpdateUser MingLiPro
*/
@file:JvmName("DtoUtils")
package com.mingliqiye.utils.dto
import java.lang.reflect.Field
/**
* 克隆一个对象通过反射创建新实例并复制所有非静态字段值
*
* @param obj 要克隆的对象必须是非空的任意类型实例
* @return 返回一个新的对象实例其字段值与原对象一致
*/
fun <T : Any> clone(obj: T): T {
val clazz = obj.javaClass
val constructor = clazz.getDeclaredConstructor().apply {
isAccessible = true
}
val instance = constructor.newInstance()
// 遍历类及其父类的所有字段进行赋值
var currentClass: Class<*>? = clazz
while (currentClass != null) {
currentClass.declaredFields.forEach { field ->
if (!java.lang.reflect.Modifier.isStatic(field.modifiers)) {
field.isAccessible = true
field.set(instance, field.get(obj))
}
}
currentClass = currentClass.superclass
}
return instance
}
/**
* 定义 DTO 拷贝行为的枚举类型
*/
enum class DotCopyType {
/**
* 表示使用点拷贝.copy方式处理字段
*/
DOT_COPY,
/**
* 表示普通拷贝方式处理字段
*/
COPY
}
/**
* 标注用于控制 DTO 字段拷贝行为的注解
*
* @param type 指定拷贝类型默认为 COPY
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(
AnnotationTarget.TYPE_PARAMETER,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.FIELD,
AnnotationTarget.PROPERTY
)
annotation class DtoCopy(val type: DotCopyType = DotCopyType.COPY)
/**
* 将源对象转换为目标 DTO 类型的实例并根据字段名匹配拷贝字段值
*
* @param obj 源对象包含需要被拷贝的数据
* @param dtoClass 目标 DTO Class 对象
* @return 返回一个新的目标 DTO 实例字段值已从源对象拷贝
*/
fun <R : Any> toDto(obj: Any, dtoClass: Class<R>): R {
val instance = dtoClass.getDeclaredConstructor().apply {
isAccessible = true
}.newInstance()
val sourceFields = getAllFields(obj.javaClass)
for (sourceField in sourceFields) {
sourceField.isAccessible = true
val fieldName = sourceField.name
val fieldValue = sourceField.get(obj)
try {
val targetField = dtoClass.getDeclaredField(fieldName).apply {
isAccessible = true
}
if (java.lang.reflect.Modifier.isStatic(targetField.modifiers)) {
continue
}
val ta = targetField.getAnnotation(DtoCopy::class.java)
if (ta != null) {
if (ta.type == DotCopyType.DOT_COPY) {
continue
}
}
targetField.set(instance, fieldValue)
} catch (e: NoSuchFieldException) {
continue
} catch (e: IllegalArgumentException) {
continue
}
}
return instance
}
/**
* 获取指定类及其所有父类中声明的所有字段
*
* @param clazz 起始类对象
* @return 包含所有字段的列表
*/
private fun getAllFields(clazz: Class<*>): List<Field> {
val fields = mutableListOf<Field>()
var currentClass: Class<*>? = clazz
while (currentClass != null && currentClass != Any::class.java) {
fields.addAll(currentClass.declaredFields)
currentClass = currentClass.superclass
}
return fields
}

View File

@ -16,24 +16,30 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ForEach.kt
* LastUpdate 2025-09-15 12:01:36
* LastUpdate 2025-09-15 12:44:46
* UpdateUser MingLiPro
*/
@file:JvmName("ForEach")
package com.mingliqiye.utils.foreach
import com.mingliqiye.utils.functions.P1Function
import com.mingliqiye.utils.functions.P1RFunction
import com.mingliqiye.utils.functions.P2Function
import com.mingliqiye.utils.functions.P2RFunction
import com.mingliqiye.utils.functions.*
/**
* 对给定的可迭代对象执行指定的操作同时处理元素值和索引
* 根据可迭代对象是否实现 RandomAccess 接口选择最优的遍历方式
*
* @param iterable 要遍历的可迭代对象
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
* @param <T> 可迭代对象中元素的类型
*/
fun <T> forEach(
iterable: Iterable<T>, action: P2Function<in T, in Int>
) {
when (iterable) {
is RandomAccess if iterable is MutableList<*> -> {
// 如果是支持随机访问的可变列表,则使用索引遍历以提高性能
val list = iterable as MutableList<T>
for (i in list.indices) {
action.call(list[i], i)
@ -41,6 +47,7 @@ fun <T> forEach(
}
is MutableList<*> -> {
// 对于普通可变列表,使用迭代器进行遍历,并手动维护索引
var index = 0
val it = iterable.iterator()
while (it.hasNext()) {
@ -50,6 +57,7 @@ fun <T> forEach(
}
else -> {
// 对于其他类型的可迭代对象,使用增强 for 循环并手动维护索引
var index = 0
for (element in iterable) {
action.call(element, index)
@ -64,29 +72,41 @@ fun <T> forEach(
* 根据可迭代对象是否实现 RandomAccess 接口选择最优的遍历方式
*
* @param iterable 要遍历的可迭代对象
* @param action 要对每个元素执行的操作只接收元素值作为参数
* @param <T> 可迭代对象中元素的类型
**/
* @param action 要对每个元素执行的操作只接收元素值作为参数
* @param <T> 可迭代对象中元素的类型
*/
fun <T> forEach(
iterable: Iterable<T>, action: P1Function<in T>
) {
if (iterable is RandomAccess) {
// 如果是支持随机访问的列表,则使用索引遍历以提高性能
val list = iterable as MutableList<T>
for (i in list.indices) {
action.call(list[i])
}
} else {
// 否则使用增强 for 循环进行遍历
for (element in iterable) {
action.call(element)
}
}
}
fun <T> forEach(
/**
* 对给定的可迭代对象执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
* 根据可迭代对象是否实现 RandomAccess 接口选择最优的遍历方式
*
* @param iterable 要遍历的可迭代对象
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
* @param <T> 可迭代对象中元素的类型
*/
fun <T> forEachB(
iterable: Iterable<T>, action: P2RFunction<in T, in Int, out Boolean>
) {
when (iterable) {
is RandomAccess if iterable is MutableList<*> -> {
// 如果是支持随机访问的可变列表,则使用索引遍历以提高性能
val list = iterable as MutableList<T>
for (i in list.indices) {
if (action.call(list[i], i)) return
@ -94,6 +114,7 @@ fun <T> forEach(
}
is MutableList<*> -> {
// 对于普通可变列表,使用迭代器进行遍历,并手动维护索引
var index = 0
val it = iterable.iterator()
while (it.hasNext()) {
@ -103,6 +124,7 @@ fun <T> forEach(
}
else -> {
// 对于其他类型的可迭代对象,使用增强 for 循环并手动维护索引
var index = 0
for (element in iterable) {
if (action.call(element, index)) return
@ -114,242 +136,574 @@ fun <T> forEach(
/**
* 对给定的可迭代对象执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
* 根据可迭代对象是否实现 RandomAccess 接口选择最优的遍历方式
*
* @param iterable 要遍历的可迭代对象
* @param action 要对每个元素执行的操作只接收元素值作为参数
* @param <T> 可迭代对象中元素的类型
**/
fun <T> forEach(
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
* @param <T> 可迭代对象中元素的类型
*/
fun <T> forEachB(
iterable: Iterable<T>, action: P1RFunction<in T, out Boolean>
) {
if (iterable is RandomAccess) {
// 如果是支持随机访问的列表,则使用索引遍历以提高性能
val list = iterable as MutableList<T>
for (i in list.indices) {
if (action.call(list[i])) return
}
} else {
// 否则使用增强 for 循环进行遍历
for (element in iterable) {
if (action.call(element)) return
}
}
}
/**
* 对给定的数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
* @param <T> 数组中元素的类型
*/
fun <T> forEach(
array: Array<T>, action: P2Function<in T, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的数组执行指定的操作仅处理元素值
*
* @param array 要遍历的数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
* @param <T> 数组中元素的类型
*/
fun <T> forEach(
array: Array<T>, action: P1Function<in T>
) {
forEach(array.toList(), action)
}
fun <T> forEach(
/**
* 对给定的数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
* @param <T> 数组中元素的类型
*/
fun <T> forEachB(
array: Array<T>, action: P2RFunction<in T, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun <T> forEach(
/**
* 对给定的数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
* @param <T> 数组中元素的类型
*/
fun <T> forEachB(
array: Array<T>, action: P1RFunction<in T, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的字节数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的字节数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: ByteArray, action: P2Function<in Byte, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的字节数组执行指定的操作仅处理元素值
*
* @param array 要遍历的字节数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: ByteArray, action: P1Function<in Byte>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的字节数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的字节数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: ByteArray, action: P2RFunction<in Byte, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的字节数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的字节数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: ByteArray, action: P1RFunction<in Byte, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的字符数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的字符数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: CharArray, action: P2Function<in Char, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的字符数组执行指定的操作仅处理元素值
*
* @param array 要遍历的字符数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: CharArray, action: P1Function<in Char>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的字符数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的字符数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: CharArray, action: P2RFunction<in Char, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的字符数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的字符数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: CharArray, action: P1RFunction<in Char, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的整型数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的整型数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: IntArray, action: P2Function<in Int, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的整型数组执行指定的操作仅处理元素值
*
* @param array 要遍历的整型数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: IntArray, action: P1Function<in Int>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的整型数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的整型数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: IntArray, action: P2RFunction<in Int, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的整型数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的整型数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: IntArray, action: P1RFunction<in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的长整型数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的长整型数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: LongArray, action: P2Function<in Long, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的长整型数组执行指定的操作仅处理元素值
*
* @param array 要遍历的长整型数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: LongArray, action: P1Function<in Long>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的长整型数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的长整型数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: LongArray, action: P2RFunction<in Long, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的长整型数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的长整型数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: LongArray, action: P1RFunction<in Long, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的短整型数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的短整型数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: ShortArray, action: P2Function<in Short, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的短整型数组执行指定的操作仅处理元素值
*
* @param array 要遍历的短整型数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: ShortArray, action: P1Function<in Short>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的短整型数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的短整型数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: ShortArray, action: P2RFunction<in Short, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的短整型数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的短整型数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: ShortArray, action: P1RFunction<in Short, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的浮点数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的浮点数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: FloatArray, action: P2Function<in Float, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的浮点数组执行指定的操作仅处理元素值
*
* @param array 要遍历的浮点数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: FloatArray, action: P1Function<in Float>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的浮点数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的浮点数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: FloatArray, action: P2RFunction<in Float, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的浮点数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的浮点数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: FloatArray, action: P1RFunction<in Float, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的双精度浮点数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的双精度浮点数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: DoubleArray, action: P2Function<in Double, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的双精度浮点数组执行指定的操作仅处理元素值
*
* @param array 要遍历的双精度浮点数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: DoubleArray, action: P1Function<in Double>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的双精度浮点数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的双精度浮点数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: DoubleArray, action: P2RFunction<in Double, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的双精度浮点数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的双精度浮点数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: DoubleArray, action: P1RFunction<in Double, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的布尔数组执行指定的操作同时处理元素值和索引
*
* @param array 要遍历的布尔数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数
*/
fun forEach(
array: BooleanArray, action: P2Function<in Boolean, in Int>
) {
forEach(array.toList(), action)
}
/**
* 对给定的布尔数组执行指定的操作仅处理元素值
*
* @param array 要遍历的布尔数组
* @param action 要对每个元素执行的操作只接收元素值作为参数
*/
fun forEach(
array: BooleanArray, action: P1Function<in Boolean>
) {
forEach(array.toList(), action)
}
fun forEach(
/**
* 对给定的布尔数组执行指定的操作同时处理元素值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的布尔数组
* @param action 要对每个元素执行的操作接收元素值和索引作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: BooleanArray, action: P2RFunction<in Boolean, in Int, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
fun forEach(
/**
* 对给定的布尔数组执行指定的操作仅处理元素值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的布尔数组
* @param action 要对每个元素执行的操作只接收元素值作为参数返回 Boolean 表示是否提前终止
*/
fun forEachB(
array: BooleanArray, action: P1RFunction<in Boolean, out Boolean>
) {
forEach(array.toList(), action)
forEachB(array.toList(), action)
}
/**
* 对给定的键值对集合执行指定的操作同时处理键值和索引
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的键值对集合
* @param action 要对每个键值对执行的操作接收键值和索引作为参数返回 Boolean 表示是否提前终止
* @param <K> 键的类型
* @param <V> 值的类型
* @param <A> 集合的具体类型
*/
fun <K, V, A : Collection<Map.Entry<K, V>>> forEachB(
array: A, action: P3RFunction<in K, in V, in Int, out Boolean>
) {
forEachB(array, P2RFunction<Map.Entry<K, V>, Int, Boolean> { p1, p2 -> action.call(p1.key, p1.value, p2) })
}
/**
* 对给定的映射执行指定的操作同时处理键值和索引
* 如果操作返回 true则提前终止遍历
*
* @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键值和索引作为参数返回 Boolean 表示是否提前终止
* @param <K> 键的类型
* @param <V> 值的类型
* @param <A> 映射的具体类型
*/
fun <K, V, A : Map<K, V>> forEachB(
map: A, action: P3RFunction<in K, in V, in Int, out Boolean>
) {
forEachB(map.entries, P2RFunction<Map.Entry<K, V>, Int, Boolean> { p1, p2 -> action.call(p1.key, p1.value, p2) })
}
/**
* 对给定的键值对集合执行指定的操作仅处理键和值
*
* @param array 要遍历的键值对集合
* @param action 要对每个键值对执行的操作接收键和值作为参数
* @param <K> 键的类型
* @param <V> 值的类型
* @param <A> 集合的具体类型
*/
fun <K, V, A : Collection<Map.Entry<K, V>>> forEachMap(
array: A, action: P2Function<in K, in V>
) {
forEach(array, P1Function<Map.Entry<K, V>> { p1 -> action.call(p1.key, p1.value) })
}
/**
* 对给定的映射执行指定的操作仅处理键和值
*
* @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键和值作为参数
* @param <K> 键的类型
* @param <V> 值的类型
* @param <A> 映射的具体类型
*/
fun <K, V, A : Map<K, V>> forEachMap(
map: A, action: P2Function<in K, in V>
) {
forEach(map.entries, P1Function<Map.Entry<K, V>> { p1 -> action.call(p1.key, p1.value) })
}
/**
* 对给定的键值对集合执行指定的操作仅处理键和值
* 如果操作返回 true则提前终止遍历
*
* @param array 要遍历的键值对集合
* @param action 要对每个键值对执行的操作接收键和值作为参数返回 Boolean 表示是否提前终止
* @param <K> 键的类型
* @param <V> 值的类型
* @param <A> 集合的具体类型
*/
fun <K, V, A : Collection<Map.Entry<K, V>>> forEachMapB(
array: A, action: P2RFunction<in K, in V, out Boolean>
) {
forEachB(array, P1RFunction<Map.Entry<K, V>, Boolean> { p1 -> action.call(p1.key, p1.value) })
}
/**
* 对给定的映射执行指定的操作仅处理键和值
* 如果操作返回 true则提前终止遍历
*
* @param map 要遍历的映射
* @param action 要对每个键值对执行的操作接收键和值作为参数返回 Boolean 表示是否提前终止
* @param <K> 键的类型
* @param <V> 值的类型
* @param <A> 映射的具体类型
*/
fun <K, V, A : Map<K, V>> forEachMapB(
map: A, action: P2RFunction<in K, in V, out Boolean>
) {
forEachB(map.entries, P1RFunction<Map.Entry<K, V>, Boolean> { p1 -> action.call(p1.key, p1.value) })
}

View File

@ -16,25 +16,22 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile HashUtils.kt
* LastUpdate 2025-09-15 09:38:04
* LastUpdate 2025-09-19 20:24:33
* UpdateUser MingLiPro
*/
@file:JvmName("HashUtils")
package com.mingliqiye.utils.hash
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.mindrot.jbcrypt.BCrypt
import com.mingliqiye.utils.base.BASE16
import com.mingliqiye.utils.bcrypt.checkpw
import com.mingliqiye.utils.bcrypt.hashpw
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.security.Security
private val _addProvider = run {
Security.addProvider(BouncyCastleProvider())
}
/**
* 计算指定文件的哈希值
@ -74,15 +71,7 @@ fun calculateFileHash(file: File, algorithm: String): String {
* @return 对应的十六进制字符串
*/
private fun bytesToHex(bytes: ByteArray): String {
val hexString = StringBuilder(2 * bytes.size)
for (b in bytes) {
val hex = Integer.toHexString(0xff and b.toInt())
if (hex.length == 1) {
hexString.append('0')
}
hexString.append(hex)
}
return hexString.toString()
return BASE16.encode(bytes)
}
/**
@ -92,7 +81,7 @@ private fun bytesToHex(bytes: ByteArray): String {
* @return 加密后的 BCrypt 哈希字符串
*/
fun bcrypt(string: String): String {
return BCrypt.hashpw(string, BCrypt.gensalt())
return hashpw(string)
}
/**
@ -103,5 +92,5 @@ fun bcrypt(string: String): String {
* @return 如果匹配返回 true否则返回 false
*/
fun checkBcrypt(string: String, bcrypted: String): Boolean {
return BCrypt.checkpw(string, bcrypted)
return checkpw(string, bcrypted)
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile IO.kt
* LastUpdate 2025-09-20 16:03:14
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.io
fun Any?.println() {
IO.println(this)
}
class IO {
companion object {
@JvmStatic
fun print(vararg args: Any?) {
printA(" ", *args)
}
@JvmStatic
fun println(vararg args: Any?) {
printlnA(" ", *args)
}
@JvmStatic
fun printlnA(sp: String, vararg args: Any?) {
printA(" ", *args)
kotlin.io.println()
}
@JvmStatic
fun printA(sp: String = "", vararg args: Any?) {
if (args.isEmpty()) {
kotlin.io.println()
}
val sb = StringBuilder()
for (i in args.indices) {
sb.append(args[i])
if (i < args.size - 1) sb.append(sp)
}
kotlin.io.print(sb)
}
}
}

View File

@ -15,24 +15,36 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonException.java
* LastUpdate 2025-09-09 09:25:08
* CurrentFile InputStreamUtils.kt
* LastUpdate 2025-09-15 17:26:34
* UpdateUser MingLiPro
*/
@file:JvmName("InputStreamUtils")
package com.mingliqiye.utils.json;
package com.mingliqiye.utils.io
public class JsonException extends RuntimeException {
import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.Charset
public JsonException(String message) {
super(message);
}
public JsonException(String message, Throwable cause) {
super(message, cause);
}
public JsonException(Throwable cause) {
this(cause.getMessage(), cause);
}
fun InputStream.readAllText(charset: Charset = Charsets.UTF_8): String {
return this.readAllBytes().toString(charset)
}
fun InputStream.readAllBytes(): ByteArray {
return this.readBytes()
}
fun InputStream.exportBytes(out: OutputStream) {
out.write(this.readAllBytes())
out.flush()
}
fun InputStream.readToList(): List<Byte> {
return this.readBytes().toList()
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile FieldStructure.kt
* LastUpdate 2025-09-14 18:19:29
* LastUpdate 2025-09-15 22:32:50
* UpdateUser MingLiPro
*/

View File

@ -0,0 +1,231 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile GsonJsonApi.kt
* LastUpdate 2025-09-15 22:07:43
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
import com.google.gson.*
import com.mingliqiye.utils.json.converters.JsonConverter
import com.mingliqiye.utils.json.converters.JsonStringConverter
class GsonJsonApi : JsonApi {
private var gsonUnicode: Gson
private var gsonPretty: Gson
private var gsonPrettyUnicode: Gson
private var gson: Gson
constructor() {
gson = GsonBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
gsonUnicode = GsonBuilder()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
gsonPretty = GsonBuilder()
.setPrettyPrinting()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
gsonPrettyUnicode = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
}
constructor(gson: Gson) {
this.gson = gson
.newBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
this.gsonUnicode = gson
.newBuilder()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
this.gsonPretty = gson
.newBuilder()
.setPrettyPrinting()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
this.gsonPrettyUnicode = gson
.newBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()
}
override fun <T> parse(json: String, clazz: Class<T>): T {
return gson.fromJson(json, clazz)
}
override fun <T> parse(json: String, type: JsonTypeReference<T>): T {
return gson.fromJson(json, type.type)
}
override fun format(obj: Any): String {
return gson.toJson(obj)
}
override fun formatUnicode(obj: Any): String {
return gsonUnicode.toJson(obj)
}
override fun formatPretty(obj: Any): String {
return gsonPretty.toJson(obj)
}
override fun formatPrettyUnicode(obj: Any): String {
return gsonPrettyUnicode.toJson(obj)
}
override fun isValidJson(json: String): Boolean {
return try {
JsonParser.parseString(json)
true
} catch (e: JsonSyntaxException) {
false
} catch (e: Exception) {
false
}
}
override fun merge(vararg jsons: String): String {
val merged = JsonObject()
for (json in jsons) {
if (json.isNullOrEmpty()) {
continue
}
try {
val obj = JsonParser.parseString(json).asJsonObject
for (key in obj.keySet()) {
merged.add(key, obj.get(key))
}
} catch (e: Exception) {
// 忽略无效的 JSON 字符串
}
}
return gson.toJson(merged)
}
override fun getNodeValue(json: String, path: String): String? {
return try {
var element = JsonParser.parseString(json)
val paths = path.split("\\.".toRegex()).toTypedArray()
var current = element
for (p in paths) {
if (current.isJsonObject) {
current = current.asJsonObject.get(p)
} else {
return null
}
if (current == null) {
return null
}
}
if (current.isJsonPrimitive) current.asString else current.toString()
} catch (e: Exception) {
null
}
}
override fun updateNodeValue(json: String, path: String, newValue: Any): String {
return try {
val obj = JsonParser.parseString(json).asJsonObject
val paths = path.split("\\.".toRegex()).toTypedArray()
var current = obj
// 导航到倒数第二层
for (i in 0 until paths.size - 1) {
val p = paths[i]
if (!current.has(p) || !current.get(p).isJsonObject) {
current.add(p, JsonObject())
}
current = current.getAsJsonObject(p)
}
// 设置最后一层的值
val lastPath = paths[paths.size - 1]
val element = gson.toJsonTree(newValue)
current.add(lastPath, element)
gson.toJson(obj)
} catch (e: Exception) {
json
}
}
override fun <T, D> convert(source: T, destinationClass: Class<D>): D {
val json = gson.toJson(source)
return gson.fromJson(json, destinationClass)
}
override fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D {
val json = gson.toJson(source)
return gson.fromJson(json, destinationType.type)
}
override fun addJsonConverter(c: JsonConverter<*, *>) {
c.getStringConverter()?.let {
gson = gson
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
gsonUnicode = gsonUnicode
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
gsonPretty = gsonPretty
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
gsonPrettyUnicode = gsonPrettyUnicode
.newBuilder()
.registerTypeAdapter(
it.tClass,
it.gsonJsonStringConverterAdapter
)
.create()
}
}
override fun addJsonStringConverter(c: JsonStringConverter<*>) {
addJsonConverter(c)
}
}

View File

@ -0,0 +1,301 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JacksonJsonApi.kt
* LastUpdate 2025-09-15 22:07:43
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectReader
import com.fasterxml.jackson.databind.node.ObjectNode
import com.mingliqiye.utils.json.converters.JsonConverter
import com.mingliqiye.utils.json.converters.JsonStringConverter
import java.io.IOException
/**
* 基于Jackson的JSON处理实现类提供JSON字符串解析格式化合并节点操作等功能
*/
class JacksonJsonApi : JsonApi {
private val objectMapper: ObjectMapper
/**
* 使用默认的ObjectMapper构造实例
*/
constructor() {
this.objectMapper = ObjectMapper()
}
/**
* 使用指定的ObjectMapper构造实例
*
* @param objectMapper 自定义的ObjectMapper实例
*/
constructor(objectMapper: ObjectMapper) {
this.objectMapper = objectMapper.copy()
}
/**
* 将JSON字符串解析为指定类型的对象
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象类型
* @param <T> 泛型参数表示目标对象类型
* @return 解析后的对象
* @throws JsonException 当解析失败时抛出异常
*/
override fun <T> parse(json: String, clazz: Class<T>): T {
return try {
objectMapper.readValue(json, clazz)
} catch (e: IOException) {
throw JsonException("Failed to parse JSON string", e)
}
}
/**
* 将JSON字符串解析为复杂泛型结构的对象如ListMap等
*
* @param json JSON字符串
* @param type 泛型类型引用
* @param <T> 泛型参数表示目标对象类型
* @return 解析后的对象
* @throws JsonException 当解析失败时抛出异常
*/
override fun <T> parse(json: String, type: JsonTypeReference<T>): T {
return try {
val reader: ObjectReader = objectMapper.readerFor(
objectMapper.constructType(type.type)
)
reader.readValue(json)
} catch (e: IOException) {
throw JsonException("Failed to parse JSON string", e)
}
}
/**
* 将对象格式化为JSON字符串
*
* @param `object` 待格式化的对象
* @return 格式化后的JSON字符串
* @throws JsonException 当格式化失败时抛出异常
*/
override fun format(obj: Any): String {
return try {
objectMapper.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
throw JsonException(
"Failed to format object to JSON string",
e
)
}
}
override fun formatUnicode(obj: Any): String {
return try {
objectMapper
.writer()
.with(JsonGenerator.Feature.ESCAPE_NON_ASCII)
.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
throw JsonException(e)
}
}
/**
* 将对象格式化为美化带缩进的JSON字符串
*
* @param `object` 待格式化的对象
* @return 美化后的JSON字符串
* @throws JsonException 当格式化失败时抛出异常
*/
override fun formatPretty(obj: Any): String {
return try {
objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
throw JsonException(
"Failed to format object to pretty JSON string",
e
)
}
}
override fun formatPrettyUnicode(obj: Any): String {
return try {
objectMapper
.writerWithDefaultPrettyPrinter()
.with(JsonGenerator.Feature.ESCAPE_NON_ASCII)
.writeValueAsString(obj)
} catch (e: JsonProcessingException) {
throw JsonException(
"Failed to format object to pretty JSON string",
e
)
}
}
/**
* 判断给定字符串是否是有效的JSON格式
*
* @param json 待验证的字符串
* @return 如果是有效JSON返回true否则返回false
*/
override fun isValidJson(json: String): Boolean {
return try {
objectMapper.readTree(json)
true
} catch (e: Exception) {
false
}
}
/**
* 合并多个JSON字符串为一个JSON对象
*
* @param jsons 多个JSON字符串
* @return 合并后的JSON字符串
* @throws JsonException 当合并失败时抛出异常
*/
override fun merge(vararg jsons: String): String {
val result: ObjectNode = objectMapper.createObjectNode()
for (json in jsons) {
try {
val node: JsonNode = objectMapper.readTree(json)
if (node.isObject) {
result.setAll<JsonNode>(node as ObjectNode)
}
} catch (e: IOException) {
// 忽略无效的JSON字符串
}
}
return try {
objectMapper.writeValueAsString(result)
} catch (e: JsonProcessingException) {
throw JsonException("Failed to merge JSON strings", e)
}
}
/**
* 获取JSON字符串中指定路径的节点值
*
* @param json JSON字符串
* @param path 节点路径使用"."分隔
* @return 节点值的文本表示如果路径不存在则返回null
* @throws JsonException 当获取节点值失败时抛出异常
*/
override fun getNodeValue(json: String, path: String): String? {
return try {
var node: JsonNode = objectMapper.readTree(json)
val paths: Array<String> = path.split("\\.".toRegex()).toTypedArray()
for (p in paths) {
node = node.get(p)
}
node.asText()
} catch (e: IOException) {
throw JsonException("Failed to get node value", e)
}
}
/**
* 更新JSON字符串中指定路径的节点值
*
* @param json JSON字符串
* @param path 节点路径使用"."分隔
* @param newValue 新的节点值
* @return 更新后的JSON字符串
* @throws JsonException 当更新节点值失败时抛出异常
*/
override fun updateNodeValue(json: String, path: String, newValue: Any): String {
return try {
val node: JsonNode = objectMapper.readTree(json)
if (node is ObjectNode) {
val objectNode: ObjectNode = node
val paths: Array<String> = path.split("\\.".toRegex()).toTypedArray()
var current: JsonNode = objectNode
// 导航到目标节点的父节点
for (i in 0 until paths.size - 1) {
current = current.get(paths[i])
if (current !is ObjectNode) {
return json // 路径不存在或无效
}
}
// 更新值
if (current is ObjectNode) {
val parent: ObjectNode = current
parent.set<JsonNode>(
paths[paths.size - 1],
objectMapper.valueToTree(newValue)
)
}
objectMapper.writeValueAsString(objectNode)
}
json
} catch (e: IOException) {
throw JsonException("Failed to update node value", e)
}
}
/**
* 在不同对象类型之间进行转换
*
* @param source 源对象
* @param destinationClass 目标对象类型
* @param <T> 源对象类型
* @param <D> 目标对象类型
* @return 转换后的对象
*/
override fun <T, D> convert(source: T, destinationClass: Class<D>): D {
return objectMapper.convertValue(source, destinationClass)
}
/**
* 在不同泛型对象类型之间进行转换
*
* @param source 源对象
* @param destinationType 目标对象的泛型类型引用
* @param <T> 源对象类型
* @param <D> 目标对象类型
* @return 转换后的对象
*/
override fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D {
return objectMapper.convertValue(
source,
objectMapper.constructType(destinationType.type)
)
}
override fun addJsonConverter(c: JsonConverter<*, *>) {
c.getStringConverter()?.let {
objectMapper.registerModule(it.jacksonJsonStringConverterAdapter.jacksonModule)
}
}
override fun addJsonStringConverter(c: JsonStringConverter<*>) {
addJsonConverter(c)
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonApi.kt
* LastUpdate 2025-09-15 11:10:59
* LastUpdate 2025-09-15 22:32:50
* UpdateUser MingLiPro
*/
@ -306,8 +306,8 @@ interface JsonApi {
* @param <T> 泛型参数表示List中元素的类型
* @return 解析后的List集合
</T> */
fun <T> parseList(json: String, elementType: Class<T>): MutableList<T> {
return parse<MutableList<T>>(json, JsonTypeUtils.listType<T>(elementType))
fun <T> parseList(json: String, elementType: Class<T>): List<T> {
return parse(json, type = listType(elementType))
}
/**
@ -372,7 +372,7 @@ interface JsonApi {
* @param path 节点路径"user.name"
* @return 节点值的字符串表示
*/
fun getNodeValue(json: String, path: String): String
fun getNodeValue(json: String, path: String): String?
/**
* 更新JSON字符串中指定路径节点的值

View File

@ -15,19 +15,18 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Players.java
* LastUpdate 2025-09-09 08:37:33
* CurrentFile JsonException.kt
* LastUpdate 2025-09-15 22:32:50
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.minecraft.slp;
package com.mingliqiye.utils.json
import lombok.Data;
class JsonException : RuntimeException {
@Data
public class Players {
constructor(message: String) : super(message)
private int max;
private int online;
private PlayerSample[] sample;
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(cause: Throwable) : this(cause.message ?: "", cause)
}

View File

@ -0,0 +1,164 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonTypeReference.kt
* LastUpdate 2025-09-15 22:32:50
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*
/**
* 通用的 JSON 类型引用类用于在运行时保留泛型类型信息
* 适用于所有 JSON JacksonGsonFastjson
*
* @param <T> 引用的泛型类型
*/
abstract class JsonTypeReference<T> : Comparable<JsonTypeReference<T>> {
open var type: Type = Any::class.java
/**
* 构造函数通过反射获取泛型类型信息
* 仅供内部匿名子类使用
*/
protected constructor() {
val superClass: Type = this.javaClass.genericSuperclass
// 检查是否为匿名子类,防止直接实例化导致无法获取泛型信息
if (superClass is Class<*>) {
throw IllegalArgumentException(
"必须使用匿名子类方式创建 JsonTypeReference" +
"例如: new JsonTypeReference<List<String>>() {}"
)
}
this.type = (superClass as ParameterizedType).actualTypeArguments[0]
}
/**
* 构造函数直接指定类型
* @param type 具体的类型信息
*/
protected constructor(type: Type) {
this.type = Objects.requireNonNull(type, "Type cannot be null")
}
/**
* 创建类型引用实例
* @param <T> 目标类型
* @return 类型引用实例
*/
companion object {
@JvmStatic
fun <T> of(): JsonTypeReference<T> {
return object : JsonTypeReference<T>() {}
}
/**
* 根据 Class 创建类型引用
* @param clazz 目标类
* @param <T> 目标类型
* @return 类型引用实例
*/
@JvmStatic
fun <T> of(clazz: Class<T>): JsonTypeReference<T> {
return object : JsonTypeReference<T>(clazz) {}
}
/**
* 根据 Type 创建类型引用
* @param type 目标类型
* @param <T> 目标类型
* @return 类型引用实例
*/
@JvmStatic
fun <T> of(type: Type): JsonTypeReference<T> {
return object : JsonTypeReference<T>(type) {}
}
}
/**
* 获取原始类型去掉泛型参数的类型
* @return 原始类型 Class
*/
@Suppress("UNCHECKED_CAST")
fun getRawType(): Class<T> {
var rawType: Type = type
// 如果是参数化类型,则提取原始类型部分
if (type is ParameterizedType) {
rawType = (type as ParameterizedType).rawType
}
if (rawType is Class<*>) {
return rawType as Class<T>
}
throw IllegalStateException("无法获取原始类型: $type")
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this.javaClass != other.javaClass) return false
val that = other as JsonTypeReference<*>
// 对于 ParameterizedType需要更完整的比较
if (this.type is ParameterizedType && that.type is ParameterizedType) {
val thisParamType = this.type as ParameterizedType
val thatParamType = that.type as ParameterizedType
return (
Objects.equals(
thisParamType.rawType,
thatParamType.rawType
) &&
thisParamType.actualTypeArguments.contentEquals(thatParamType.actualTypeArguments) &&
Objects.equals(
thisParamType.ownerType,
thatParamType.ownerType
)
)
}
return Objects.equals(type, that.type)
}
override fun hashCode(): Int {
if (type is ParameterizedType) {
val paramType = type as ParameterizedType
return Objects.hash(
paramType.rawType,
paramType.actualTypeArguments.contentHashCode(),
paramType.ownerType
)
}
return Objects.hash(type)
}
override fun toString(): String {
return "JsonTypeReference{$type}"
}
override fun compareTo(other: JsonTypeReference<T>): Int {
return this.type.toString().compareTo(other.type.toString())
}
}

View File

@ -0,0 +1,201 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonTypeUtils.kt
* LastUpdate 2025-09-17 11:12:06
* UpdateUser MingLiPro
*/
@file:JvmName("JsonTypeUtils")
package com.mingliqiye.utils.json
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*
/**
* 检查给定的类型是否是指定类或其子类/实现类
*
* @param type 要检查的类型
* @param expectedClass 期望匹配的类
* @return 如果类型匹配则返回 true否则返回 false
*/
fun isTypeOf(type: Type, expectedClass: Class<*>): Boolean {
return when (type) {
is Class<*> -> expectedClass.isAssignableFrom(type)
is ParameterizedType -> isTypeOf(type.rawType, expectedClass)
else -> false
}
}
/**
* 获取泛型类型的参数类型
*
* @param type 泛型类型
* @param index 参数索引从0开始
* @return 指定位置的泛型参数类型
* @throws IllegalArgumentException 当无法获取指定索引的泛型参数时抛出异常
*/
fun getGenericParameter(type: Type, index: Int): Type {
if (type is ParameterizedType) {
val typeArgs = type.actualTypeArguments
if (index >= 0 && index < typeArgs.size) {
return typeArgs[index]
}
}
throw IllegalArgumentException(
"无法获取泛型参数: $type at index $index"
)
}
/**
* 获取类型名称支持普通类和泛型类型
*
* @param type 类型对象
* @return 类型名称字符串
*/
fun getTypeName(type: Type): String {
return when (type) {
is Class<*> -> type.simpleName
is ParameterizedType -> {
val rawType = type.rawType as Class<*>
val typeArgs = type.actualTypeArguments
val sb = StringBuilder(rawType.simpleName)
sb.append("<")
for (i in typeArgs.indices) {
if (i > 0) sb.append(", ")
sb.append(getTypeName(typeArgs[i]))
}
sb.append(">")
sb.toString()
}
else -> type.typeName
}
}
/**
* 创建一个表示数组类型的引用对象
*
* @param componentType 数组元素的类型
* @param <T> 元素类型
* @return 表示数组类型的 JsonTypeReference 对象
*/
fun <T> arrayType(componentType: Class<T>): JsonTypeReference<Array<T>> {
return object : JsonTypeReference<Array<T>>() {
private val arrayType: Type = java.lang.reflect.Array.newInstance(
componentType, 0
).javaClass
override var type: Type = Any::class.java
get() = object : ParameterizedType {
private val actualTypeArguments = arrayOf<Type>(componentType)
override fun getActualTypeArguments(): Array<Type> {
return actualTypeArguments
}
override fun getRawType(): Type {
return arrayType
}
override fun getOwnerType(): Type? {
return null
}
}
}
}
/**
* 创建一个表示 List 类型的引用对象
*
* @param componentType List 中元素的类型
* @param <T> 元素类型
* @return 表示 List 类型的 JsonTypeReference 对象
* @throws IllegalArgumentException 如果 componentType null则抛出异常
*/
fun <T> listType(componentType: Class<T>): JsonTypeReference<List<T>> {
return object : JsonTypeReference<List<T>>() {
override var type: Type = Any::class.java
get() = object : ParameterizedType {
override fun getActualTypeArguments(): Array<Type> {
return arrayOf(componentType)
}
override fun getRawType(): Type {
return List::class.java
}
override fun getOwnerType(): Type? {
return null
}
}
}
}
/**
* 创建一个表示 Map 类型的引用对象
*
* @param keyType Map 键的类型
* @param valueType Map 值的类型
* @param <K> 键类型
* @param <V> 值类型
* @return 表示 Map 类型的 JsonTypeReference 对象
* @throws IllegalArgumentException 如果 keyType valueType null则抛出异常
*/
fun <K, V> MapType(keyType: Class<K>, valueType: Class<V>): JsonTypeReference<Map<K, V>> {
return object : JsonTypeReference<Map<K, V>>() {
override var type: Type = Any::class.java
get() = object : ParameterizedType {
override fun getActualTypeArguments(): Array<Type> {
return arrayOf(keyType, valueType)
}
override fun getRawType(): Type {
return Map::class.java
}
override fun getOwnerType(): Type? {
return null
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ParameterizedType) return false
val that = other
return (Objects.equals(
rawType,
that.rawType
) && actualTypeArguments.contentEquals(that.actualTypeArguments) && Objects.equals(
ownerType,
that.ownerType
))
}
override fun hashCode(): Int {
return (actualTypeArguments.contentHashCode() xor Objects.hashCode(rawType) xor Objects.hashCode(
ownerType
))
}
}
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile JsonStringConverter.kt
* LastUpdate 2025-09-15 11:03:53
* LastUpdate 2025-09-17 19:09:17
* UpdateUser MingLiPro
*/
@ -35,7 +35,6 @@ import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.DateTime.Companion.parse
import com.mingliqiye.utils.time.Formatter
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.UUID.Companion.of
import java.io.IOException
@ -219,7 +218,7 @@ class DateTimeJsonConverter : JsonStringConverter<DateTime>() {
if (obj == null) {
return null
}
return obj.format(Formatter.STANDARD_DATETIME)
return obj.format()
}
override fun deConvert(obj: String?): DateTime? {
@ -227,9 +226,7 @@ class DateTimeJsonConverter : JsonStringConverter<DateTime>() {
return null
}
return parse(
obj,
Formatter.STANDARD_DATETIME_MILLISECOUND7,
true
obj
)
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Loggers.kt
* LastUpdate 2025-09-14 18:19:29
* LastUpdate 2025-09-18 09:30:48
* UpdateUser MingLiPro
*/
@ -416,6 +416,6 @@ class MingLiLoggerFactory {
}
}
val mingLiLoggerFactory = MingLiLoggerFactory()
val mingLiLoggerFactory: MingLiLoggerFactory by lazy { MingLiLoggerFactory() }

View File

@ -16,18 +16,20 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Main.kt
* LastUpdate 2025-09-15 09:53:43
* LastUpdate 2025-09-20 13:22:11
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils
@file:JvmName("Main")
package com.mingliqiye.utils.main
import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.stream.SuperStream
fun main() {
AutoConfiguration.printBanner()
println(DateTime.now())
}
val data = SuperStream.of(Array(0) { 1 })
fun test() {
println(data)
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MetaData.kt
* LastUpdate 2025-09-20 10:45:43
* UpdateUser MingLiPro
*/
@file:JvmName("MetaData")
package com.mingliqiye.utils.metadata
import com.mingliqiye.utils.resource.ResourceUtils
import java.util.stream.Collectors.toMap
fun getMetaData(): Map<String, String> {
return ResourceUtils.getStringResource("/META-INF/meta-data").split("\n").stream().map {
if (it.isBlank()) {
return@map null
}
val split = it.split("=")
if (split.size == 2) {
split[0] to split[1]
} else {
return@map null
}
}.filter { it != null }.collect(toMap({ it!!.first }, { it!!.second }))
}
class MingliUtilsMetaData {
var buildTime: String = ""
var groupId: String = ""
var artifactId: String = ""
var version: String = ""
var buildJdkVersion: String = ""
var author: String = ""
var website: String = ""
}
val mingliUtilsMetaData: MingliUtilsMetaData by lazy {
val metaData = getMetaData()
MingliUtilsMetaData().apply {
buildTime = metaData["buildTime"] ?: ""
groupId = metaData["groupId"] ?: ""
artifactId = metaData["artifactId"] ?: ""
version = metaData["version"] ?: ""
buildJdkVersion = metaData["buildJdkVersion"] ?: ""
author = metaData["author"] ?: ""
website = metaData["website"] ?: ""
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTimeTypeHandler.kt
* LastUpdate 2025-09-14 18:19:29
* LastUpdate 2025-09-15 13:53:53
* UpdateUser MingLiPro
*/
@file:JvmName("DateTimeConvertor")
@ -65,8 +65,8 @@ class DateTimeTypeHandler : BaseTypeHandler<DateTime>() {
override fun setNonNullParameter(
ps: PreparedStatement,
i: Int,
parameter: DateTime, // 移除了 ?,因为这是 non-null 方法
jdbcType: JdbcType
parameter: DateTime,
jdbcType: JdbcType?
) {
// 使用 setObject 允许传入 null由数据库处理
ps.setObject(i, toLocalDateTime(parameter))

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUIDTypeHandler.kt
* LastUpdate 2025-09-14 18:19:29
* LastUpdate 2025-09-15 13:54:18
* UpdateUser MingLiPro
*/
@file:JvmName("UUIDConvertor")
@ -112,7 +112,7 @@ class UUIDBinaryTypeHandler : BaseTypeHandler<UUID>() {
* @param jdbcType JDBC类型
*/
override fun setNonNullParameter(
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType?
) {
ps.setBytes(i, uuidToByteArray(parameter))
}
@ -172,7 +172,7 @@ class UUIDStringTypeHandler : BaseTypeHandler<UUID>() {
* @param jdbcType JDBC类型
*/
override fun setNonNullParameter(
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType?
) {
ps.setString(i, uuidToString(parameter))
}
@ -234,7 +234,7 @@ class UUIDTypeHandler : BaseTypeHandler<UUID>() {
* @param jdbcType JDBC类型
*/
override fun setNonNullParameter(
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType?
) {
ps.setObject(i, parameter.getUuid())
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MysqlUUIDBinaryTypeHandler.kt
* LastUpdate 2025-09-14 18:19:29
* LastUpdate 2025-09-15 13:54:29
* UpdateUser MingLiPro
*/
@ -72,7 +72,7 @@ class MysqlUUIDBinaryTypeHandler : BaseTypeHandler<UUID>() {
ps: PreparedStatement,
i: Int,
parameter: UUID,
jdbcType: JdbcType
jdbcType: JdbcType?
) {
ps.setBytes(i, toByteArray(parameter))
}

View File

@ -15,35 +15,28 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NetworkException.java
* LastUpdate 2025-09-09 08:37:33
* CurrentFile QueryWrapper.kt
* LastUpdate 2025-09-20 14:21:44
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network;
package com.mingliqiye.utils.mybatisplus
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper
/**
* 网络异常类用于处理网络相关的运行时异常
* BaseMapperQuery接口扩展了BaseMapper提供了通用的查询包装器功能
*
* @author MingLiPro
* @param T 实体类类型
*/
public class NetworkException extends RuntimeException {
/**
* 构造一个带有指定详细消息的网络异常
*
* @param message 异常的详细消息
*/
public NetworkException(String message) {
super(message);
}
/**
* 构造一个网络异常指定原因异常
*
* @param e 导致此异常的原因异常
*/
public NetworkException(Exception e) {
super(e);
}
interface BaseMapperQuery<T> : BaseMapper<T> {
/**
* 创建并返回一个新的QueryWrapper实例
*
* @return QueryWrapper<T> 返回类型化的查询包装器实例
*/
fun queryWrapper(): QueryWrapper<T> {
return QueryWrapper<T>()
}
}

View File

@ -0,0 +1,350 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AddressPort.kt
* LastUpdate 2025-09-15 22:01:27
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network
import java.io.Serializable
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.UnknownHostException
import java.util.regex.Pattern
/**
* 网络地址类用于表示一个网络地址IP或域名并提供相关操作
* 支持IPv4和IPv6地址的解析与验证
*
* @author MingLiPro
*/
class NetworkAddress private constructor(domip: String) : Serializable {
/**
* IPv6标识
*/
companion object {
const val IPV6 = 6
/**
* IPv4标识
*/
const val IPV4 = 4
/**
* IPv4地址正则表达式
*/
private const val IPV4REG =
"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" + "(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$"
/**
* 编译后的IPv4地址匹配模式
*/
private val IPV4_PATTERN = Pattern.compile(IPV4REG)
/**
* IPv6地址正则表达式
*/
private const val IPV6REG =
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" + "^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" + "|" + "^(::)$|" + "^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" + "^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$"
/**
* 编译后的IPv6地址匹配模式
*/
private val IPV6_PATTERN = Pattern.compile(IPV6REG)
/**
* 静态工厂方法创建 NetworkAddress 实例
*
* @param domip 可能是IP地址或域名的字符串
* @return 新建的 NetworkAddress 实例
*/
@JvmStatic
fun of(domip: String): NetworkAddress {
return NetworkAddress(domip)
}
/**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
*
* @param inetAddress InetAddress 对象
* @return 新建的 NetworkAddress 实例
*/
@JvmStatic
fun of(inetAddress: InetAddress): NetworkAddress {
return NetworkAddress(inetAddress.hostAddress)
}
/**
* 从DNS服务器解析域名获取对应的IP地址
*
* @param domain 域名
* @return 解析出的第一个IP地址
* @throws UnknownHostException 如果域名无法解析
*/
@JvmStatic
@Throws(UnknownHostException::class)
fun getHostIp(domain: String): String {
val addresses = InetAddress.getAllByName(domain.trim())
return addresses[0].hostAddress
}
/**
* 检测给定字符串是否为有效的IPv4或IPv6地址
*
* @param ip 要检测的IP地址字符串
* @return 4 表示IPv46 表示IPv6
* @throws NetworkException 如果IP格式无效
*/
@JvmStatic
fun testIp(ip: String?): Int {
if (ip == null) {
throw NetworkException("IP地址不能为null")
}
val trimmedIp = ip.trim()
// 判断是否匹配IPv4格式
if (IPV4_PATTERN.matcher(trimmedIp).matches()) {
return IPV4
}
// 判断是否匹配IPv6格式
if (IPV6_PATTERN.matcher(trimmedIp).matches()) {
return IPV6
}
// 不符合任一格式时抛出异常
throw NetworkException(
"[$ip] 不是有效的IPv4或IPv6地址"
)
}
}
/**
* IP地址类型4 表示 IPv46 表示 IPv6
*/
var iPv: Int = 0
private set
/**
* IP地址字符串
*/
var ip: String? = null
private set
/**
* 域名如果输入的是域名
*/
private var domain: String? = null
/**
* 标识是否是域名解析来的IP
*/
private var isdom = false
/**
* 构造方法根据传入的字符串判断是IP地址还是域名并进行相应处理
*
* @param domip 可能是IP地址或域名的字符串
*/
init {
try {
// 尝试将输入识别为IP地址
this.iPv = testIp(domip)
this.ip = domip
} catch (e: NetworkException) {
try {
// 如果不是有效IP则尝试作为域名解析
val ips = getHostIp(domip)
this.iPv = testIp(ips)
this.ip = ips
this.isdom = true
this.domain = domip
} catch (ex: UnknownHostException) {
throw NetworkException(ex)
}
}
}
/**
* 将当前 NetworkAddress 转换为 InetAddress 对象
*
* @return InetAddress 对象
*/
fun toInetAddress(): InetAddress {
try {
return InetAddress.getByName(if (ip != null) ip else domain)
} catch (e: UnknownHostException) {
throw RuntimeException(e)
}
}
/**
* 返回 NetworkAddress 的字符串表示形式
*
* @return 字符串表示
*/
override fun toString(): String {
return if (isdom) "NetworkAddress(IP='$ip',type='$iPv',domain='$domain')"
else "NetworkAddress(IP='$ip',type='$iPv')"
}
}
class NetworkPort : Serializable {
val port: Int
constructor(port: Int) {
testPort(port)
this.port = port
}
companion object {
fun testPort(port: Int) {
// 验证端口号范围是否在0-65535之间
if (port !in 0..65535) {
throw NetworkException("$port 不是正确的端口号")
}
}
}
}
class NetworkException : RuntimeException {
/**
* 构造一个带有指定详细消息的网络异常
*
* @param message 异常的详细消息
*/
constructor(message: String?) : super(message)
/**
* 构造一个网络异常指定原因异常
*
* @param e 导致此异常的原因异常
*/
constructor(e: Exception?) : super(e)
}
/**
* IP和端口聚集类用于封装网络地址与端口信息
* 该类提供了与InetSocketAddress之间的相互转换功能
*
* @author MingLiPro
* @see java.net.InetSocketAddress
*/
class NetworkEndpoint private constructor(
val networkAddress: NetworkAddress, val networkPort: NetworkPort
) : Serializable {
companion object {
/**
* 根据给定的InetSocketAddress对象创建NetworkEndpoint实例
*
* @param address InetSocketAddress对象
* @return 新建的NetworkEndpoint实例
* @see java.net.InetSocketAddress
*/
@JvmStatic
fun of(address: InetSocketAddress): NetworkEndpoint {
return NetworkEndpoint(
NetworkAddress.of(address.hostString), NetworkPort(address.port)
)
}
/**
* 根据主机名或IP字符串和端口号创建NetworkEndpoint实例
*
* @param s 主机名或IP地址字符串
* @param i 端口号
* @return 新建的NetworkEndpoint实例
*/
@JvmStatic
fun of(s: String, i: Int): NetworkEndpoint {
val networkAddress = NetworkAddress.of(s)
val networkPort = NetworkPort(i)
return NetworkEndpoint(networkAddress, networkPort)
}
/**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080"
*
* @param s "host:port"格式的字符串
* @return 新建的NetworkEndpoint实例
*/
@JvmStatic
fun of(s: String): NetworkEndpoint {
val lastColonIndex = s.lastIndexOf(':')
return of(
s.take(lastColonIndex), s.substring(lastColonIndex + 1).toInt()
)
}
}
/**
* 将当前NetworkEndpoint转换为InetSocketAddress对象
*
* @return 对应的InetSocketAddress对象
* @see InetSocketAddress
*/
fun toInetSocketAddress(): InetSocketAddress {
return InetSocketAddress(
networkAddress.toInetAddress(), networkPort.port
)
}
/**
* 将当前NetworkEndpoint转换为"host:port"格式的字符串
* 例如"127.0.0.1:25563"
*
* @return 格式化后的字符串
*/
fun toHostPortString(): String {
return "${networkAddress.ip}:${networkPort.port}"
}
/**
* 返回NetworkEndpoint的详细字符串表示形式
* 格式NetworkEndpoint(IP=...,Port=...,Endpoint=...)
*
* @return 包含详细信息的字符串
*/
override fun toString(): String {
return "NetworkEndpoint(IP=${networkAddress.ip},Port=${networkPort.port},Endpoint=${toHostPortString()})"
}
/**
* 获取主机名或IP地址字符串
*
* @return 主机名或IP地址
*/
fun host(): String {
return networkAddress.ip ?: ""
}
/**
* 获取端口号
*
* @return 端口号
*/
fun port(): Int {
return networkPort.port
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NumberUtils.kt
* LastUpdate 2025-09-16 15:59:45
* UpdateUser MingLiPro
*/
@file:JvmName("NumberUtils")
package com.mingliqiye.utils.number
import java.io.IOException
import java.io.InputStream
/**
* 从输入流中读取一个变长整数VarNumber
*
* 变长整数使用可变长度编码方式每个字节的最高位表示是否还有后续字节
* - 如果最高位为1则表示还有下一个字节
* - 如果最高位为0则表示当前字节是最后一个字节
*
* @param input 输入流用于读取数据
* @param size 最大允许读取的字节数默认为8即Long类型的最大长度
* @return 解码后的长整型数值
* @throws IOException 当读取过程中发生IO异常或到达流末尾时抛出
*/
@Throws(IOException::class)
fun readVarNumber(input: InputStream, size: Int = 10): Long {
var numRead = 0
var result: Long = 0
var read: Byte
do {
read = input.read().let {
if (it == -1) {
throw IOException("Reached end of stream")
}
it.toByte()
}
// 将当前字节的有效7位数据左移相应位数并与结果进行或运算
result = result or ((read.toLong() and 127) shl (7 * numRead))
numRead++
if (numRead > size) {
throw IOException("VarNumber is too big")
}
} while ((read.toLong() and 128) != 0L)
return result
}
/**
* 从输入流中读取一个变长整数VarInt最大长度限制为4个字节
*
* @param input 输入流用于读取数据
* @return 解码后的整型数值
* @throws IOException 当读取过程中发生IO异常时抛出
*/
@Throws(IOException::class)
fun readVarInt(input: InputStream): Int {
return readVarNumber(input, size = 4).toInt()
}
/**
* 从输入流中读取一个变长短整数VarShort最大长度限制为2个字节
*
* @param input 输入流用于读取数据
* @return 解码后的短整型数值
* @throws IOException 当读取过程中发生IO异常时抛出
*/
@Throws(IOException::class)
fun readVarShort(input: InputStream): Short {
return readVarNumber(input, size = 2).toShort()
}
/**
* 从输入流中读取一个变长长整数VarLong最大长度默认为8个字节
*
* @param input 输入流用于读取数据
* @return 解码后的长整型数值
* @throws IOException 当读取过程中发生IO异常时抛出
*/
@Throws(IOException::class)
fun readVarLong(input: InputStream): Long {
return readVarNumber(input)
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile OsPath.kt
* LastUpdate 2025-09-15 08:59:15
* LastUpdate 2025-09-18 09:47:43
* UpdateUser MingLiPro
*/
@ -26,9 +26,8 @@ package com.mingliqiye.utils.path
import java.io.File
import java.net.URI
import java.nio.file.*
import java.util.*
import java.util.function.Consumer
import java.nio.file.Path
import java.nio.file.Paths
class OsPath private constructor(private val path: Path) : Path by path {
@ -58,48 +57,4 @@ class OsPath private constructor(private val path: Path) : Path by path {
return OsPath(Paths.get(""))
}
}
override fun getParent(): Path? {
var parent = path.parent
if (parent == null) {
parent = path.toAbsolutePath().parent
}
return parent
}
override fun toRealPath(vararg options: LinkOption): Path {
return OsPath(path.toRealPath(*options))
}
override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>): WatchKey {
return path.register(watcher, *events)
}
override fun register(
watcher: WatchService,
events: Array<out WatchEvent.Kind<*>>,
vararg modifiers: WatchEvent.Modifier
): WatchKey {
return path.register(watcher, events, *modifiers)
}
override fun iterator(): MutableIterator<Path> {
return path.iterator()
}
override fun compareTo(other: Path): Int {
return path.compareTo(other)
}
override fun toString(): String {
return path.toString()
}
override fun forEach(action: Consumer<in Path>) {
path.forEach(action)
}
override fun spliterator(): Spliterator<Path> {
return path.spliterator()
}
}

View File

@ -16,13 +16,15 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile RandomBytes.kt
* LastUpdate 2025-09-15 09:54:33
* LastUpdate 2025-09-16 17:42:26
* UpdateUser MingLiPro
*/
@file:JvmName("RandomBytes")
package com.mingliqiye.utils.random
import java.security.SecureRandom
/**
* 生成指定长度的随机字节数组
* @param length 数组长度
@ -78,3 +80,14 @@ fun randomByteNoHave(from: Byte, to: Byte): Byte {
val randomValue = randomIntNoHave(fromInt, toInt)
return (randomValue and 0xFF).toByte()
}
val secureRandom: SecureRandom by lazy {
SecureRandom.getInstanceStrong()
}
fun randomByteSecure(size: Int): ByteArray {
val bytes = ByteArray(size)
secureRandom.nextBytes(bytes)
return bytes
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ResourceUtils.kt
* LastUpdate 2025-09-20 10:26:47
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.resource
import java.io.IOException
class ResourceUtils {
companion object {
@JvmStatic
@Throws(IOException::class)
fun getResource(resourceName: String): ByteArray {
return getResource(resourceName, ResourceUtils::class.java)
}
@JvmStatic
@Throws(IOException::class)
fun getResource(resourceName: String, clazz: Class<*>): ByteArray {
return clazz.getResourceAsStream(resourceName)?.use {
it.readBytes()
} ?: throw IOException("Resource not found: $resourceName")
}
@JvmStatic
@Throws(IOException::class)
fun getStringResource(resourceName: String): String {
return getStringResource(resourceName, ResourceUtils::class.java)
}
@JvmStatic
@Throws(IOException::class)
fun getStringResource(resourceName: String, clazz: Class<*>): String {
return clazz.getResourceAsStream(resourceName)?.use {
it.readBytes().toString(charset = Charsets.UTF_8)
} ?: throw IOException("Resource not found: $resourceName")
}
@JvmStatic
@Throws(IOException::class)
fun getStringResourceCallers(resourceName: String): String {
return getStringResource(resourceName, getCallerClass())
}
@JvmStatic
@Throws(IOException::class)
fun getResourceCallers(resourceName: String): ByteArray {
return getResource(resourceName, getCallerClass())
}
private fun getCallerClass(): Class<*> {
val stackTrace = Thread.currentThread().stackTrace
for (i in 2 until stackTrace.size) {
val className = stackTrace[i].className
try {
val clazz = Class.forName(className)
if (clazz != ResourceUtils::class.java && clazz != Companion::class.java) {
return clazz
}
} catch (e: ClassNotFoundException) {
continue
}
}
return ResourceUtils::class.java
}
}
}

View File

@ -1,6 +1,29 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AesUtils.kt
* LastUpdate 2025-09-19 20:18:09
* UpdateUser MingLiPro
*/
@file:JvmName("AesUtils")
package com.mingliqiye.utils.security
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
@ -9,8 +32,8 @@ const val AES_GCM_NO_PADDING = "AES/GCM/NoPadding"
const val AES_GCM_NO_PADDING_IV_LENGTH = 12
const val AES_GCM_NO_PADDING_TAG_LENGTH = 16
fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray,iv: ByteArray): ByteArray {
val secretKeySpec = createSecretKeySpec(ALGORITHM,key)
fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
val secretKeySpec = createSecretKeySpec(ALGORITHM, key)
val cipher = Cipher.getInstance(AES_GCM_NO_PADDING)
val gcmParameterSpec = GCMParameterSpec(
AES_GCM_NO_PADDING_TAG_LENGTH * 8,
@ -19,17 +42,13 @@ fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray,iv: ByteArray): ByteAr
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec)
return cipher.doFinal(src)
}
fun encryptAesGcmNoPadding(src: ByteArray, key: String,iv: ByteArray): ByteArray {
fun encryptAesGcmNoPadding(src: ByteArray, key: String, iv: ByteArray): ByteArray {
return encryptAesGcmNoPadding(src, key.toByteArray(), iv)
}
fun encryptAesGcmNoPadding(src: String, key: String,iv: ByteArray): ByteArray {
fun encryptAesGcmNoPadding(src: String, key: String, iv: ByteArray): ByteArray {
return encryptAesGcmNoPadding(src.toByteArray(), key.toByteArray(), iv)
}
fun main() {
val iv = getRandomBytes(16)
println(encryptAesGcmNoPadding("mingliqiye","key", iv))
}

View File

@ -1,3 +1,25 @@
/*
* Copyright 2025 mingliqiye
*
* 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
*
* 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.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SecureUtils.kt
* LastUpdate 2025-09-15 22:32:50
* UpdateUser MingLiPro
*/
@file:JvmName("SecureUtils")
package com.mingliqiye.utils.security
@ -25,9 +47,9 @@ fun createSecretKey(algorithm: String, data: String): ByteArray {
}
fun createSecretKeySpec(algorithm: String, data: String): SecretKey {
return SecretKeySpec( createSecretKey(algorithm,data), algorithm)
return SecretKeySpec(createSecretKey(algorithm, data), algorithm)
}
fun createSecretKeySpec(algorithm: String, data: ByteArray): SecretKey {
return SecretKeySpec( createSecretKey(algorithm,data), algorithm)
}
return SecretKeySpec(createSecretKey(algorithm, data), algorithm)
}

View File

@ -16,14 +16,17 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AutoConfiguration.kt
* LastUpdate 2025-09-15 08:51:52
* LastUpdate 2025-09-20 10:47:00
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.springboot.autoconfigure
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.system.getJdkVersion
import com.mingliqiye.utils.system.computerName
import com.mingliqiye.utils.system.getPid
import com.mingliqiye.utils.system.jdkVersion
import com.mingliqiye.utils.system.userName
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.Formatter
import org.springframework.context.annotation.ComponentScan
@ -31,7 +34,7 @@ import java.io.IOException
@org.springframework.boot.autoconfigure.AutoConfiguration
@ComponentScan(
"com.mingliqiye.utils.bean.springboot",
"com.mingliqiye.utils.springboot.bean",
"com.mingliqiye.utils.springboot.converters"
)
open class AutoConfiguration {
@ -51,10 +54,7 @@ open class AutoConfiguration {
fun printBanner() {
val bannerBuilder = StringBuilder(banner)
try {
val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data")
if (inputStream == null) {
return
}
val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data") ?: return
inputStream.use { stream ->
var readlen: Int
val buffer = ByteArray(1024)
@ -63,8 +63,11 @@ open class AutoConfiguration {
metaData.append(String(buffer, 0, readlen))
}
val da = metaData.toString().split("\n").toMutableList()
da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7))
da.add("jdkRuntime=" + getJdkVersion())
da.add("jdkRuntime=$jdkVersion")
da.add("pid=$getPid")
da.add("computerName=$computerName")
da.add("userName=$userName")
da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true))
da.forEach { s: String ->
val d = s.trim { it <= ' ' }.split("=".toRegex(), 2).toTypedArray()
if (d.size >= 2) {

View File

@ -16,11 +16,11 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SpringBeanUtils.kt
* LastUpdate 2025-09-14 22:10:45
* LastUpdate 2025-09-19 20:07:08
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.bean.springboot
package com.mingliqiye.utils.springboot.bean
import org.springframework.beans.BeansException
import org.springframework.context.ApplicationContext

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile StringUtils.kt
* LastUpdate 2025-09-14 21:46:14
* LastUpdate 2025-09-18 09:26:41
* UpdateUser MingLiPro
*/
@file:JvmName("StringUtils")
@ -28,14 +28,17 @@ import com.mingliqiye.utils.logger.mingLiLoggerFactory
val log = mingLiLoggerFactory.getLogger("StringUtils")
val NULLISH_STRINGS = setOf("null", "NaN", "undefined", "None", "none")
/**
* 判断`字符串`是否为空
*
* @param str 待判断的字符串
* @return `true`: `false`: 非空
*/
fun isEmpty(str: String?): Boolean {
return str?.isEmpty() != null
@JvmName("isEmpty")
fun String?.isNullish(): Boolean {
return this == null || this.isBlank() || this in NULLISH_STRINGS
}
/**
@ -163,12 +166,51 @@ fun String.stringBuilder(): java.lang.StringBuilder {
return StringBuilder(this)
}
/**
* 将字符串按照指定分隔符进行分割
* @param str 需要分割的字符串
* @param separator 分割符
* @return 分割后的字符串列表
*/
fun split(str: String, separator: String): List<String> {
return str.split(separator)
}
fun List<String>.join(separator: String): String {
return this.joinToString(separator)
/**
* 将列表中的元素使用指定分隔符连接成字符串
* @param separator 连接分隔符
* @param getstring 转换函数将列表元素转换为字符串默认使用toString()方法
* @return 连接后的字符串
*/
fun <T> List<T>.join(separator: String, getstring: (T) -> String = { it.toString() }): String {
// 使用StringBuilder构建结果字符串
val sb = StringBuilder()
for (i in this.indices) {
sb.append(this[i])
// 除了最后一个元素外,都在后面添加分隔符
if (i != this.size - 1) {
sb.append(separator)
}
}
return sb.toString()
}
/**
* 使用当前字符串作为分隔符将列表中的元素连接成字符串
* @param list 需要连接的元素列表
* @param getstring 转换函数将列表元素转换为字符串默认使用toString()方法
* @return 连接后的字符串
*/
fun <T> String.join(list: List<T>, getstring: (T) -> String = { it.toString() }): String {
// 使用StringBuilder构建结果字符串
val sb = StringBuilder()
for (i in list.indices) {
sb.append(getstring(list[i]))
// 除了最后一个元素外,都在后面添加当前字符串作为分隔符
if (i != list.size - 1) {
sb.append(this)
}
}
return sb.toString()
}

View File

@ -16,26 +16,34 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile SystemUtil.kt
* LastUpdate 2025-09-15 11:18:34
* LastUpdate 2025-09-16 17:36:11
* UpdateUser MingLiPro
*/
@file:JvmName("SystemUtils")
package com.mingliqiye.utils.system
import com.mingliqiye.utils.random.randomByteSecure
import java.lang.management.ManagementFactory
import java.net.Inet4Address
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
private val osName: String? = System.getProperties().getProperty("os.name")
/**
* 操作系统名称属性延迟初始化
*/
val osName: String? by lazy {
System.getProperties().getProperty("os.name")
}
/**
* 判断当前操作系统是否为Windows系统
*
* @return 如果是Windows系统返回true否则返回false
*/
fun isWindows(): Boolean {
return osName != null && osName.startsWith("Windows")
val isWindows: Boolean by lazy {
osName != null && osName!!.startsWith("Windows")
}
/**
@ -43,8 +51,8 @@ fun isWindows(): Boolean {
*
* @return 如果是Mac系统返回true否则返回false
*/
fun isMac(): Boolean {
return osName != null && osName.startsWith("Mac")
val isMac: Boolean by lazy {
osName != null && osName!!.startsWith("Mac")
}
/**
@ -52,31 +60,30 @@ fun isMac(): Boolean {
*
* @return 如果是Unix/Linux系统返回true否则返回false
*/
fun isUnix(): Boolean {
val isUnix: Boolean by lazy {
if (osName == null) {
return false
false
} else {
(osName!!.startsWith("Linux") || osName!!.startsWith("AIX") || osName!!.startsWith("SunOS") || osName!!.startsWith(
"Mac OS X"
) || osName!!.startsWith(
"FreeBSD"
))
}
return (osName.startsWith("Linux") || osName.startsWith("AIX") || osName.startsWith("SunOS") || osName.startsWith("Mac OS X") || osName.startsWith(
"FreeBSD"
))
}
/**
* 获取JDK版本号
*
* @return JDK版本号字符串
* JDK版本号属性延迟初始化
*/
fun getJdkVersion(): String? {
return System.getProperty("java.specification.version")
val jdkVersion: String? by lazy {
System.getProperty("java.specification.version")
}
/**
* 获取Java版本号的整数形式
*
* @return Java版本号的整数形式81117
* Java版本号的整数形式属性延迟初始化
*/
fun getJavaVersionAsInteger(): Int {
val version = getJdkVersion()
val javaVersionAsInteger: Int by lazy {
val version = jdkVersion
if (version == null || version.isEmpty()) {
throw IllegalStateException(
"Unable to determine Java version from property 'java.specification.version'"
@ -98,7 +105,7 @@ fun getJavaVersionAsInteger(): Int {
}
version.take(2)
}
return uversion.toInt()
uversion.toInt()
}
/**
@ -106,18 +113,17 @@ fun getJavaVersionAsInteger(): Int {
*
* @return 如果JDK版本大于8返回true否则返回false
*/
fun isJdk8Plus(): Boolean {
return getJavaVersionAsInteger() > 8
val isJdk8Plus: Boolean by lazy {
javaVersionAsInteger > 8
}
/**
* 获取本地IP地址数组
* 本地IP地址数组延迟初始化
*
* @return 本地IP地址字符串数组
* @throws RuntimeException 当获取网络接口信息失败时抛出
*/
fun getLocalIps(): Array<String> {
return try {
val localIps: Array<String> by lazy {
try {
val ipList: MutableList<String> = ArrayList()
val interfaces = NetworkInterface.getNetworkInterfaces()
@ -145,22 +151,18 @@ fun getLocalIps(): Array<String> {
}
/**
* 获取本地IP地址列表
*
* @return 本地IP地址的字符串列表
* 本地IP地址列表延迟初始化
*/
fun getLocalIpsByList(): List<String> {
return getLocalIps().toList()
val localIpsByList: List<String> by lazy {
localIps.toList()
}
/**
* 获取本地回环地址
*
* @return 回环地址字符串通常为"127.0.0.1"
* 本地回环地址数组延迟初始化
*/
fun getLoopbackIps(): Array<String> {
val loopbackIps: Array<String> by lazy {
val strings: MutableList<String> = ArrayList(3)
return try {
return@lazy try {
val interfaces = NetworkInterface.getNetworkInterfaces()
while (interfaces.hasMoreElements()) {
@ -183,11 +185,193 @@ fun getLoopbackIps(): Array<String> {
}
/**
* 获取本地回环地址IP列表
*
* @return 本地回环地址IP字符串列表的副本
* 本地回环地址IP列表延迟初始化
*/
fun getLoopbackIpsByList(): List<String> {
// 将本地回环地址IP数组转换为列表并返回
return getLoopbackIps().toList()
val loopbackIpsByList: List<String> by lazy {
loopbackIps.toList()
}
/**
* 获取当前进程的PID
*
* @return 进程ID如果无法获取则返回-1
*/
val getPid: Long by lazy {
try {
val name = ManagementFactory.getRuntimeMXBean().name
val index = name.indexOf('@')
if (index > 0) {
name.take(index).toLong()
} else {
-1L
}
} catch (e: Exception) {
-1L
}
}
/**
* 获取当前进程的PID字符串形式
*
* @return 进程ID字符串如果无法获取则返回"-1"
*/
val pidAsString: String by lazy {
try {
val name = ManagementFactory.getRuntimeMXBean().name
val index = name.indexOf('@')
if (index > 0) {
name.take(index)
} else {
"-1"
}
} catch (e: Exception) {
"-1"
}
}
/**
* 获取计算机名
*
* @return 计算机名如果无法获取则返回"unknown"
*/
val computerName: String by lazy {
try {
var name = System.getenv("COMPUTERNAME")
if (name.isNullOrBlank()) {
name = System.getenv("HOSTNAME")
}
if (name.isNullOrBlank()) {
name = InetAddress.getLocalHost().hostName
}
name ?: "unknown"
} catch (e: Exception) {
"unknown"
}
}
/**
* 获取当前用户名
*
* @return 当前用户名如果无法获取则返回"unknown"
*/
val userName: String by lazy {
try {
getEnvVar("USERNAME")
?: getEnvVar("USER")
?: System.getProperty("user.name")
?: "unknown"
} catch (e: SecurityException) {
"unknown"
} catch (e: Exception) {
"unknown"
}
}
private fun getEnvVar(name: String): String? {
return try {
System.getenv(name)?.takeIf { it.isNotBlank() }
} catch (e: SecurityException) {
null
}
}
/**
* 获取本机MAC地址的字节数组形式
*
* @return MAC地址字节数组如果无法获取则返回空数组
*/
val macAddressBytes: ByteArray by lazy {
try {
val interfaces = NetworkInterface.getNetworkInterfaces()
while (interfaces.hasMoreElements()) {
val networkInterface = interfaces.nextElement()
// 跳过回环接口和虚拟接口
if (networkInterface.isLoopback || networkInterface.isVirtual || !networkInterface.isUp) {
continue
}
val mac = networkInterface.hardwareAddress
if (mac != null && mac.isNotEmpty()) {
return@lazy mac
}
}
randomByteSecure(6)
} catch (e: SocketException) {
randomByteSecure(6)
} catch (e: Exception) {
randomByteSecure(6)
}
}
/**
* 获取本机MAC地址的十六进制字符串列表形式
*
* @return MAC地址字符串列表每个元素表示一个字节的十六进制值大写如果无法获取则返回空列表
*/
val macAddressStringList: List<String> by lazy {
val macBytes = macAddressBytes
if (macBytes.isEmpty()) {
return@lazy emptyList()
}
macBytes.map { String.format("%02X", it) }
}
/**
* 获取本机MAC地址的格式化字符串形式 "00:11:22:33:44:55"
*
* @return 格式化的MAC地址字符串如果无法获取则返回空字符串
*/
val macAddressFormattedString: String by lazy {
val macBytes = macAddressBytes
if (macBytes.isEmpty()) {
return@lazy ""
}
macBytes.joinToString(":") { String.format("%02X", it) }
}
/**
* 获取所有网络接口的MAC地址映射
*
* @return Map结构key为网络接口名称value为对应的MAC地址字节数组
*/
val allMacAddresses: Map<String, ByteArray> by lazy {
try {
val result = mutableMapOf<String, ByteArray>()
val interfaces = NetworkInterface.getNetworkInterfaces()
while (interfaces.hasMoreElements()) {
val networkInterface = interfaces.nextElement()
// 跳过回环接口和虚拟接口
if (networkInterface.isLoopback || networkInterface.isVirtual) {
continue
}
val mac = networkInterface.hardwareAddress
if (mac != null && mac.isNotEmpty()) {
result[networkInterface.name] = mac
}
}
result
} catch (e: SocketException) {
emptyMap()
} catch (e: Exception) {
emptyMap()
}
}
/**
* 获取所有网络接口的MAC地址字符串列表映射
*
* @return Map结构key为网络接口名称value为对应的MAC地址字符串列表
*/
val allMacAddressesStringList: Map<String, List<String>> by lazy {
allMacAddresses.mapValues { entry ->
entry.value.map { String.format("%02X", it) }
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTime.kt
* LastUpdate 2025-09-15 09:57:50
* LastUpdate 2025-09-17 19:06:39
* UpdateUser MingLiPro
*/
@ -27,8 +27,8 @@ import com.mingliqiye.utils.jna.NANOS_PER_100NS
import com.mingliqiye.utils.jna.WinKernel32Api
import com.mingliqiye.utils.jna.getWinKernel32Apis
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.system.getJavaVersionAsInteger
import com.mingliqiye.utils.system.isWindows
import com.mingliqiye.utils.system.javaVersionAsInteger
import org.slf4j.Logger
import java.io.Serializable
import java.time.LocalDateTime
@ -46,8 +46,7 @@ import kotlin.time.Instant
* @author MingLiPro
*/
class DateTimeOffset private constructor(
val offsetType: ChronoUnit,
val offset: Long
val offsetType: ChronoUnit, val offset: Long
) {
companion object {
@ -96,6 +95,8 @@ enum class Formatter(private val value: String) {
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
STANDARD_DATETIME_MILLISECOUND8("yyyy-MM-dd HH:mm:ss.SSSSSSSS"),
STANDARD_DATETIME_MILLISECOUND9("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"),
/**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
@ -152,6 +153,7 @@ enum class Formatter(private val value: String) {
*/
COMPACT_DATETIME("yyyyMMddHHmmss");
private val len: Int = value.length
fun getLen(): Int {
@ -179,15 +181,11 @@ enum class Formatter(private val value: String) {
* @see Instant
*/
class DateTime private constructor(
private var localDateTime: LocalDateTime,
private val zoneId: ZoneId = ZoneId.systemDefault()
private var localDateTime: LocalDateTime, private val zoneId: ZoneId = ZoneId.systemDefault()
) : Serializable {
companion object {
private val WIN_KERNEL_32_API: WinKernel32Api? = if (
getJavaVersionAsInteger() == 8 &&
isWindows()
) {
private val WIN_KERNEL_32_API: WinKernel32Api? = if (javaVersionAsInteger == 8 && isWindows) {
val log: Logger = mingLiLoggerFactory.getLogger("mingli-utils DateTime")
val a = getWinKernel32Apis()
@ -219,9 +217,7 @@ class DateTime private constructor(
fun now(): DateTime {
if (WIN_KERNEL_32_API != null) {
return DateTime(
WIN_KERNEL_32_API.getTime()
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
WIN_KERNEL_32_API.getTime().atZone(ZoneId.systemDefault()).toLocalDateTime()
)
}
return DateTime(LocalDateTime.now())
@ -273,9 +269,7 @@ class DateTime private constructor(
*/
@JvmStatic
fun parse(
timestr: String,
formatter: String,
fillZero: Boolean
timestr: String, formatter: String, fillZero: Boolean
): DateTime {
return DateTime(
LocalDateTime.parse(
@ -285,6 +279,20 @@ class DateTime private constructor(
)
}
@JvmStatic
fun parse(
timestr: String
): DateTime {
val formatterString = Formatter.STANDARD_DATETIME_MILLISECOUND9.getValue()
return DateTime(
LocalDateTime.parse(
getFillZeroByLen(timestr, formatterString),
DateTimeFormatter.ofPattern(formatterString)
)
)
}
/**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例
*
@ -295,9 +303,7 @@ class DateTime private constructor(
*/
@JvmStatic
fun parse(
timestr: String,
formatter: Formatter,
fillZero: Boolean
timestr: String, formatter: Formatter, fillZero: Boolean
): DateTime {
return parse(timestr, formatter.getValue(), fillZero)
}
@ -343,18 +349,14 @@ class DateTime private constructor(
modifiedDstr += "."
}
val sb = StringBuilder(modifiedDstr)
for (i in 0 until formats.length - dstr.length) {
for (i in 0 until formats.length - sb.length) {
sb.append("0")
}
return sb.toString()
}
throw IllegalArgumentException(
String.format(
"Text: '%s' len %s < %s %s",
dstr,
dstr.length,
formats,
formats.length
"Text: '%s' len %s < %s %s", dstr, dstr.length, formats, formats.length
)
)
}
@ -384,11 +386,7 @@ class DateTime private constructor(
*/
@JvmStatic
fun of(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int
year: Int, month: Int, day: Int, hour: Int, minute: Int
): DateTime {
return DateTime(LocalDateTime.of(year, month, day, hour, minute))
}
@ -407,8 +405,7 @@ class DateTime private constructor(
// 2. 从纳秒时间戳创建 Instant
val instant = java.time.Instant.ofEpochSecond(
unixNanos / 1_000_000_000L,
unixNanos % 1_000_000_000L
unixNanos / 1_000_000_000L, unixNanos % 1_000_000_000L
)
// 3. 转换为系统默认时区的 LocalDateTime
@ -428,12 +425,7 @@ class DateTime private constructor(
*/
@JvmStatic
fun of(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int
year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int
): DateTime {
return DateTime(
LocalDateTime.of(year, month, day, hour, minute, second)
@ -454,13 +446,7 @@ class DateTime private constructor(
*/
@JvmStatic
fun of(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int,
nano: Int
year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, nano: Int
): DateTime {
return DateTime(
LocalDateTime.of(year, month, day, hour, minute, second, nano)
@ -476,9 +462,14 @@ class DateTime private constructor(
@JvmStatic
fun of(epochMilli: Long): DateTime {
return DateTime(
java.time.Instant.ofEpochMilli(epochMilli)
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
java.time.Instant.ofEpochMilli(epochMilli).atZone(ZoneId.systemDefault()).toLocalDateTime()
)
}
@JvmStatic
fun of(seconds: Long, nanos: Long): DateTime {
return DateTime(
java.time.Instant.ofEpochSecond(seconds, nanos).atZone(ZoneId.systemDefault()).toLocalDateTime()
)
}
@ -492,8 +483,7 @@ class DateTime private constructor(
@JvmStatic
fun of(epochMilli: Long, zoneId: ZoneId): DateTime {
return DateTime(
java.time.Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime(),
zoneId
java.time.Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime(), zoneId
)
}
}
@ -525,8 +515,7 @@ class DateTime private constructor(
fun add(dateTimeOffset: DateTimeOffset): DateTime {
return DateTime(
this.localDateTime.plus(
dateTimeOffset.offset,
dateTimeOffset.offsetType
dateTimeOffset.offset, dateTimeOffset.offsetType
)
)
}
@ -538,12 +527,7 @@ class DateTime private constructor(
* @return 返回修改后的 DateTime 实例
*/
fun sub(dateTimeOffset: DateTimeOffset): DateTime {
return DateTime(
this.localDateTime.plus(
-dateTimeOffset.offset,
dateTimeOffset.offsetType
)
)
return add(DateTimeOffset.of(-dateTimeOffset.offset, dateTimeOffset.offsetType))
}
/**
@ -566,6 +550,10 @@ class DateTime private constructor(
return format(formatter.getValue())
}
fun format(): String {
return format(Formatter.STANDARD_DATETIME_MILLISECOUND9.getValue(), true)
}
/**
* 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零
*
@ -602,10 +590,7 @@ class DateTime private constructor(
* @return 返回标准格式的时间字符串
*/
override fun toString(): String {
return String.format(
"DateTime(%s)",
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
)
return "DateTime(${format()})"
}
/**
@ -675,4 +660,34 @@ class DateTime private constructor(
fun getZoneId(): ZoneId {
return zoneId
}
/**
* Instant 转换为纳秒时间戳
* @throws ArithmeticException 如果结果超出 Long 范围 (-2^63 2^63-1)
*/
fun toNanoTime(): Long {
val instant = toInstant()
return try {
val secondsInNanos = Math.multiplyExact(instant.epochSecond, 1_000_000_000L)
Math.addExact(secondsInNanos, instant.nano.toLong())
} catch (e: ArithmeticException) {
throw ArithmeticException(
"无法将 Instant(${instant.epochSecond}s, ${instant.nano}ns) 转换为纳秒: ${e.message}"
)
}
}
fun to100NanoTime(): Long {
return toInstant().let {
(it.epochSecond * 10_000_000L) + (it.nano / 100L)
}
}
fun toMillisecondTime(): Long {
return toInstant().let {
(it.epochSecond * 1000L) + (it.nano / 1_000_000L)
}
}
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MysqlUUIDv1.kt
* LastUpdate 2025-09-14 18:19:29
* LastUpdate 2025-09-15 22:32:50
* UpdateUser MingLiPro
*/
@file:JvmName("MysqlUUIDv1")

View File

@ -16,70 +16,195 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUID.kt
* LastUpdate 2025-09-14 22:38:51
* LastUpdate 2025-09-19 20:22:27
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.uuid
import com.github.f4b6a3.uuid.UuidCreator
import com.mingliqiye.utils.base.BASE256
import com.mingliqiye.utils.base.BASE64
import com.mingliqiye.utils.base.BASE91
import com.mingliqiye.utils.random.randomByteSecure
import com.mingliqiye.utils.random.secureRandom
import com.mingliqiye.utils.system.macAddressBytes
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.DateTimeOffset
import java.io.Serializable
import java.nio.ByteBuffer
import java.security.MessageDigest
import java.time.temporal.ChronoUnit
import java.util.*
import java.util.UUID as JUUID
/**
* UUID 类用于生成和操作不同版本的 UUID通用唯一标识符
* 支持 UUID 的序列化转换解析和时间/版本信息提取
*/
class UUID : Serializable {
private val uuid: JUUID
private val data: ByteArray
private val mostSigBits: Long
private val leastSigBits: Long
private val version: Int
companion object {
/**
* 获取 UUIDV1 版本的随机实例
* @return UUID
* 预期 UUID 字符串中连字符的位置数组
*/
@JvmStatic
val expectedHyphenPositions = intArrayOf(8, 13, 18, 23)
/**
* UUID 纪元偏移量以天为单位
*/
@JvmStatic
val UUID_EPOCH_OFFSET = 141427L
@JvmStatic
fun ofBase64ShortString(baseShortString: String): UUID {
return UUID(BASE64.decode(baseShortString))
}
@JvmStatic
fun ofBase256ShortString(baseShortString: String): UUID {
return UUID(BASE256.decode(baseShortString))
}
@JvmStatic
fun ofBase91ShortString(baseShortString: String): UUID {
return UUID(BASE91.decode(baseShortString))
}
/**
* 生成一个 UUID V1 版本使用系统时钟随机数和 MAC 地址
* 如果 MAC 地址为空则使用随机生成的 MAC 地址
*
* @return UUID V1 实例
*/
@JvmStatic
fun getV1(): UUID {
return UUID(UuidCreator.getTimeBased())
val time = DateTime.now().add(DateTimeOffset.of(UUID_EPOCH_OFFSET, ChronoUnit.DAYS)).to100NanoTime()
val timeLow = (time and 0xFFFFFFFFL).toInt()
val timeMid = ((time shr 32) and 0xFFFFL).toShort()
val timeHighAndVersion = (((time shr 48) and 0x0FFFL) or 0x1000L).toShort()
val clockSeq = (secureRandom.nextInt(16384)) and 0x3FFF
val byteBuffer = ByteBuffer.wrap(ByteArray(16))
byteBuffer.putInt(timeLow)
byteBuffer.putShort(timeMid)
byteBuffer.putShort(timeHighAndVersion)
byteBuffer.putShort(clockSeq.toShort())
byteBuffer.put(macAddressBytes)
return UUID(byteBuffer.array())
}
@Deprecated("使用 getV1()", ReplaceWith("getV1()"), level = DeprecationLevel.WARNING)
fun getTimeBased(): UUID = getV1()
/**
* 获取 UUIDV4 版本的随机实例
* @return UUID
* 生成一个 UUID V4 版本使用加密安全的随机数
*
* @return UUID V4 实例
*/
@JvmStatic
fun getV4(): UUID {
return UUID(UuidCreator.getRandomBased())
val randomBytes = randomByteSecure(16)
randomBytes[6] = (randomBytes[6].toInt() and 0x0F).toByte()
randomBytes[6] = (randomBytes[6].toInt() or 0x40).toByte()
randomBytes[8] = (randomBytes[8].toInt() and 0x3F).toByte()
randomBytes[8] = (randomBytes[8].toInt() or 0x80).toByte()
return UUID(randomBytes)
}
/**
* 获取 UUIDV1Fast 版本的随机实例
* @return UUID
* 生成一个 UUID V3 版本基于命名空间和名称的 MD5 哈希值
*
* @param namepath 命名空间 UUID
* @param user 用户提供的字符串
* @return UUID V3 实例
*/
@JvmStatic
fun getV4Fast(): UUID {
return UUID(UuidCreator.getRandomBasedFast())
fun getV3(namepath: UUID, user: String): UUID {
val md = MessageDigest.getInstance("MD5")
val userB = user.toByteArray()
val array = md.digest(
ByteBuffer.wrap(ByteArray(16 + userB.size)).put(namepath.data).put(userB).array()
)
array[6] = (array[6].toInt() and 0x0F or 0x30).toByte()
array[8] = (array[8].toInt() and 0x3F or 0x80).toByte()
return UUID(array)
}
/**
* 从2个8个字节转换到UUID
* @param lsb 高位 8 字节的 Long
* @param msb 低位 8 字节的 Long
* @return UUID
* 生成一个 UUID V5 版本基于命名空间和名称的 SHA-1 哈希值
*
* @param namepath 命名空间 UUID
* @param user 用户提供的字符串
* @return UUID V5 实例
*/
@JvmStatic
fun of(msb: Long, lsb: Long): UUID {
return UUID(msb, lsb)
fun getV5(namepath: UUID, user: String): UUID {
val sha1 = MessageDigest.getInstance("SHA-1")
val userB = user.toByteArray()
val array = sha1.digest(
ByteBuffer.wrap(ByteArray(namepath.data.size + userB.size)).put(namepath.data).put(userB).array()
)
array[6] = (array[6].toInt() and 0x0F or 0x50).toByte()
array[8] = (array[8].toInt() and 0x3F or 0x80).toByte()
return UUID(ByteBuffer.wrap(ByteArray(16)).put(array, 0, 16).array())
}
/**
* 从字符串格式化
* @param uuid 字符串
* @return UUID
* 生成一个 UUID V6 版本使用时间戳和随机节点信息
*
* @return UUID V6 实例
*/
@JvmStatic
fun getV6(): UUID {
val timestamp = DateTime.now()
.add(DateTimeOffset.of(UUID_EPOCH_OFFSET, ChronoUnit.DAYS))
.to100NanoTime() and 0x0FFFFFFFFFFFFFFFL
val timeHigh = (timestamp ushr 12) and 0xFFFFFFFFFFFFL
val timeMid = timestamp and 0x0FFFL
val clockSeq = secureRandom.nextInt(16384) and 0x3FFF
val node = secureRandom.nextLong() and 0x0000FFFFFFFFFFFFL
val buffer = ByteBuffer.allocate(16)
buffer.putLong((timeHigh shl 16) or (0x6000L) or (timeMid and 0x0FFF))
buffer.putShort((0x8000 or (clockSeq and 0x3FFF)).toShort())
buffer.put((node shr 40).toByte())
buffer.put((node shr 32).toByte())
buffer.put((node shr 24).toByte())
buffer.put((node shr 16).toByte())
buffer.put((node shr 8).toByte())
buffer.put(node.toByte())
return UUID(buffer.array())
}
/**
* 生成一个 UUID V7 版本使用毫秒级时间戳和随机数
*
* @return UUID V7 实例
*/
@JvmStatic
fun getV7(): UUID {
val instant = DateTime.now().toMillisecondTime()
val buffer = ByteBuffer.allocate(16)
buffer.putInt((instant shr 16).toInt())
buffer.putShort((instant).toShort())
buffer.put(randomByteSecure(2))
buffer.putLong(secureRandom.nextLong())
val bytes = buffer.array()
bytes[6] = (bytes[6].toInt() and 0x0F or 0x70).toByte()
bytes[8] = (bytes[8].toInt() and 0x3F or 0x80).toByte()
return UUID(bytes)
}
/**
* 根据字符串创建 UUID 实例
*
* @param uuid UUID 字符串
* @return UUID 实例
*/
@JvmStatic
fun of(uuid: String): UUID {
@ -87,135 +212,470 @@ class UUID : Serializable {
}
/**
* 从Java的UUID
* @param uuid 字符串
* @return UUID
* 根据字节数组创建 UUID 实例
*
* @param uuid UUID 字节数组
* @return UUID 实例
*/
@JvmStatic
fun of(uuid: JUUID): UUID {
fun of(uuid: ByteArray): UUID {
return UUID(uuid)
}
/**
* 从字节码转换到UUID
* @param array 16字节
* @return UUID
* 根据高位和低位长整型创建 UUID 实例
*
* @param msb 高位长整型
* @param lsb 低位长整型
* @return UUID 实例
*/
@JvmStatic
fun of(array: ByteArray): UUID {
return UUID(array)
fun of(msb: Long, lsb: Long): UUID {
return UUID(msb, lsb)
}
fun JUUID.toMlUUID(): UUID {
return of(this)
/**
* 根据 MySQL UUID 字节数组创建 UUID 实例
*
* @param byteArray MySQL UUID 字节数组
* @return UUID 实例
*/
@JvmStatic
fun ofMysqlUUID(byteArray: ByteArray): UUID {
return UUID(mysqlToUuid(byteArray))
}
}
internal constructor(msb: Long, lsb: Long) {
uuid = JUUID(msb, lsb)
}
/**
* 根据 MySQL UUID 实例创建 UUID 实例
*
* @param uuid MySQL UUID 实例
* @return UUID 实例
*/
@JvmStatic
fun ofMysqlUUID(uuid: UUID): UUID {
return UUID(mysqlToUuid(uuid.data))
}
internal constructor(uuid: JUUID) {
this.uuid = uuid
}
/**
* 获取最大 UUID所有字节为 0xFF
*
* @return 最大 UUID 实例
*/
@JvmStatic
fun getMaxUUID(): UUID {
return UUID(
ByteArray(
16
) { 0xFF.toByte() }
)
}
internal constructor(array: ByteArray) {
val bb = ByteBuffer.wrap(array)
this.uuid = JUUID(bb.getLong(), bb.getLong())
}
/**
* 获取最小 UUID所有字节为 0x00
*
* @return 最小 UUID 实例
*/
@JvmStatic
fun getMinUUID(): UUID {
return UUID(
ByteArray(
16
) { 0x00.toByte() }
)
}
constructor(uuid: String) {
this.uuid = JUUID.fromString(uuid)
}
/**
* Java UUID 转换为自定义 UUID 实例
*
* @receiver Java UUID 实例
* @return 自定义 UUID 实例
*/
@JvmStatic
@JvmName("ofJUUID")
fun JUUID.toMLUUID(): UUID {
return UUID(this)
}
/**
* 从字符串解析 UUID 字节数组
*
* @param uuidString UUID 字符串
* @return UUID 字节数组
*/
private fun fromString(uuidString: String): ByteArray {
val cleanStr = when (uuidString.length) {
36 -> {
for (i in expectedHyphenPositions) {
if (uuidString[i] != '-') {
throw IllegalArgumentException("Invalid UUID string: $uuidString at index $i")
}
}
uuidString.replace("-", "")
}
32 -> uuidString
else -> throw IllegalArgumentException("Invalid UUID string: $uuidString")
}
// 直接按索引提取各段并校验
val segments = arrayOf(
cleanStr.take(8),
cleanStr.substring(8, 12),
cleanStr.substring(12, 16),
cleanStr.substring(16, 20),
cleanStr.substring(20, 32)
)
for (segment in segments) {
if (!segment.matches(Regex("^[0-9A-Fa-f]+$"))) {
throw IllegalArgumentException("Invalid UUID segment: $segment")
}
}
return cleanStr.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
}
/**
* MAC 地址字节数组转换为长整型
*
* @param mac MAC 地址字节数组必须为 6 字节
* @return MAC 地址对应的长整型值
*/
private fun macBytesToLong(mac: ByteArray): Long {
require(mac.size == 6) { "MAC地址必须是6字节" }
var result = 0L
for (i in 0 until 6) {
result = (result shl 8) or (mac[i].toLong() and 0xFF)
}
return result
}
}
/**
* 获取对应的字节码
* @return 字节码
* 构造函数根据字节数组初始化 UUID
*
* @param data UUID 字节数组必须为 16 字节
*/
constructor(data: ByteArray) {
require(data.size == 16) { "UUID byte array length must be 16" }
this.data = data
val bb: ByteBuffer = ByteBuffer.wrap(data)
mostSigBits = bb.long
leastSigBits = bb.long
version = (mostSigBits shr 12 and 0xF).toInt()
}
/**
* 构造函数根据高位和低位长整型初始化 UUID
*
* @param msb 高位长整型
* @param lsb 低位长整型
*/
constructor(msb: Long, lsb: Long) {
mostSigBits = msb
leastSigBits = lsb
val bb: ByteBuffer = ByteBuffer.wrap(ByteArray(16))
bb.putLong(msb)
bb.putLong(lsb)
this.data = bb.array()
version = (mostSigBits shr 12 and 0xF).toInt()
}
/**
* 构造函数根据 Java UUID 初始化自定义 UUID
*
* @param juuid Java UUID 实例
*/
constructor(juuid: JUUID) {
mostSigBits = juuid.mostSignificantBits
leastSigBits = juuid.leastSignificantBits
val bb: ByteBuffer = ByteBuffer.wrap(ByteArray(16))
bb.putLong(mostSigBits)
bb.putLong(leastSigBits)
this.data = bb.array()
version = (mostSigBits shr 12 and 0xF).toInt()
}
/**
* 构造函数根据字符串初始化 UUID
*
* @param uuid UUID 字符串
*/
constructor(uuid: String) {
fromString(uuid).let {
val bb: ByteBuffer = ByteBuffer.wrap(it)
mostSigBits = bb.long
leastSigBits = bb.long
this.data = it
version = (mostSigBits shr 12 and 0xF).toInt()
}
}
/**
* 返回 UUID 的字节数组表示
*
* @return UUID 字节数组
*/
fun toBytes(): ByteArray {
val bb = ByteBuffer.wrap(ByteArray(16))
bb.putLong(uuid.mostSignificantBits)
bb.putLong(uuid.leastSignificantBits)
return bb.array()
return data
}
/**
* 获取Java的UUID对象
* @return Java的UUID对象
* 返回 UUID 的高位长整型部分
*
* @return 高位长整型
*/
fun getMostSignificantBits(): Long {
return mostSigBits
}
/**
* 返回 UUID 的低位长整型部分
*
* @return 低位长整型
*/
fun getLeastSignificantBits(): Long {
return leastSigBits
}
/**
* 返回 UUID 的字符串表示
*
* @param isUpper 是否使用大写字母默认为 false
* @return UUID 字符串
*/
fun getString(isUpper: Boolean = false): String {
return getString().let {
if (isUpper) {
it.uppercase()
} else it
}
}
/**
* 返回 UUID 的字符串表示
*
* @param isUpper 是否使用大写字母默认为 false
* @param isnotSpace 是否移除连字符默认为 false
* @return UUID 字符串
*/
fun getString(isUpper: Boolean = false, isnotSpace: Boolean = false): String {
return getString(isUpper).let {
if (isnotSpace) {
it.replace("-", "")
} else it
}
}
/**
* 返回标准格式的 UUID 字符串带连字符
*
* @return 标准格式的 UUID 字符串
*/
// 优化后的 toString 方法
fun getString(): String {
return buildString(36) {
val msbHigh = (mostSigBits ushr 32).toInt()
val msbMid = ((mostSigBits ushr 16) and 0xFFFF).toInt()
val msbLow = (mostSigBits and 0xFFFF).toInt()
val lsbHigh = ((leastSigBits ushr 48) and 0xFFFF).toInt()
val lsbLow = (leastSigBits and 0xFFFFFFFFFFFFL)
append(msbHigh.toHexString())
append('-')
append(msbMid.toShort().toHexString(4))
append('-')
append(msbLow.toShort().toHexString(4))
append('-')
append(lsbHigh.toShort().toHexString(4))
append('-')
append(lsbLow.toHexString(12))
}
}
/**
* 将长整型转换为指定长度的十六进制字符串
*
* @param length 字符串长度
* @return 十六进制字符串
*/
private fun Long.toHexString(length: Int): String {
return this.toString(16).padStart(length, '0')
}
/**
* 将短整型转换为指定长度的十六进制字符串
*
* @param length 字符串长度
* @return 十六进制字符串
*/
private fun Short.toHexString(length: Int): String {
return this.toLong().and(0xFFFF).toString(16).padStart(length, '0')
}
/**
* 返回 MySQL 格式的 UUID 字节数组
*
* @return MySQL 格式的 UUID 字节数组
*/
fun toMysql(): ByteArray {
return mysqlToUuid(this.data)
}
/**
* 返回 MySQL 格式的 UUID 实例
*
* @return MySQL 格式的 UUID 实例
*/
fun toMysqlUUID(): UUID {
return of(mysqlToUuid(this.data))
}
/**
* 比较两个 UUID 是否相等
*
* @param other 另一个对象
* @return 如果相等则返回 true否则返回 false
*/
override fun equals(other: Any?): Boolean {
return when (other) {
is UUID -> {
this.data.contentEquals(other.data)
}
is JUUID -> {
other.mostSignificantBits == this.getMostSignificantBits() && other.leastSignificantBits == this.getLeastSignificantBits()
}
else -> {
false
}
}
}
/**
* 返回对应的 Java UUID 实例
*
* @return Java UUID 实例
*/
fun getUuid(): JUUID {
return uuid
return JUUID(mostSigBits, leastSigBits)
}
/**
* UUID 转换为字符串表示默认使用小写格式
* @param u 是否大写
* @return UUID 字符串
* 返回 UUID 的版本号
*
* @return 版本号
*/
fun getString(u: Boolean): String {
return if (u) {
uuid.toString().uppercase(Locale.ROOT)
} else {
uuid.toString()
fun getVersion(): Int {
return version
}
/**
* 计算 UUID 的哈希码
*
* @return 哈希码
*/
override fun hashCode(): Int {
return data.contentHashCode()
}
/**
* 返回 UUID 的字符串表示
*
* @return 包含 UUID 和版本号的字符串
*/
override fun toString(): String {
return "UUID(uuid=${getString()},version=${version})"
}
/**
* 根据 UUID 版本提取对应的时间信息
*
* @return 对应的时间信息
*/
fun getDateTime(): DateTime {
when (version) {
1 -> {
val timestamp =
((mostSigBits and 0x0FFFL) shl 48 or (((mostSigBits shr 16) and 0x0FFFFL) shl 32) or (mostSigBits ushr 32))
val timestampBigInt = java.math.BigInteger.valueOf(timestamp)
val nanosecondsBigInt = timestampBigInt.multiply(java.math.BigInteger.valueOf(100L))
val divisor = java.math.BigInteger.valueOf(1_000_000_000L)
val seconds = nanosecondsBigInt.divide(divisor)
val nanos = nanosecondsBigInt.remainder(divisor)
return DateTime.of(seconds.toLong(), nanos.toLong())
.sub(DateTimeOffset.of(ChronoUnit.DAYS, UUID_EPOCH_OFFSET))
}
6 -> {
val timeHigh = (
((data[0].toLong() and 0xFF) shl 40) or
((data[1].toLong() and 0xFF) shl 32) or
((data[2].toLong() and 0xFF) shl 24) or
((data[3].toLong() and 0xFF) shl 16) or
((data[4].toLong() and 0xFF) shl 8) or
(data[5].toLong() and 0xFF)
)
val timeMidAndVersion = ((data[6].toInt() and 0xFF) shl 8) or (data[7].toInt() and 0xFF)
val timeMid = timeMidAndVersion and 0x0FFF
val hundredNanosSinceUuidEpoch = (timeHigh shl 12) or timeMid.toLong()
val seconds = hundredNanosSinceUuidEpoch / 10_000_000
val nanos = (hundredNanosSinceUuidEpoch % 10_000_000) * 100
return DateTime.of(seconds, nanos).sub(DateTimeOffset.of(ChronoUnit.DAYS, UUID_EPOCH_OFFSET))
}
7 -> {
val times =
(data[0].toLong() and 0xFF shl 40) or
(data[1].toLong() and 0xFF shl 32) or
(data[2].toLong() and 0xFF shl 24) or
(data[3].toLong() and 0xFF shl 16) or
(data[4].toLong() and 0xFF shl 8) or
(data[5].toLong() and 0xFF)
return DateTime.of(times)
}
else -> {
throw IllegalArgumentException("UUID version is $version not v1 or v6 or v7 : not supported ")
}
}
}
/**
* UUID 转换为字符串表示默认使用小写格式
*
* @return UUID 字符串
*/
fun getString(): String {
return getString(false)
}
@Deprecated("使用 getString()", ReplaceWith("getString"), level = DeprecationLevel.WARNING)
fun toUUIDString(): String {
return this.getString()
}
@Deprecated("使用 getString(u:Boolean)", ReplaceWith("getString(u)"), level = DeprecationLevel.WARNING)
fun toUUIDString(u: Boolean): String {
return this.getString(u)
}
/**
* 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象
*
* @return 对应的 DateTime 对象如果 不是 时间戳V1版本 返回 null
*/
fun getDateTime(): DateTime? {
if (uuid.version() != 1) {
return null
}
return DateTime.of(uuid.timestamp() / 10000).add(
DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
)
}
/**
* 从时间戳型 UUID 中提取 MAC 地址默认使用冒号分隔符
* 提取 UUID V1 中的 MAC 地址默认使用冒号分隔符
*
* @return MAC 地址字符串
*/
fun extractMACFromUUID(): String {
return extractMACFromUUID(null)
fun getMac(): String {
return getMac(":")
}
fun getBase64ShortString(): String {
return BASE64.encode(data).substring(0, 22)
}
fun getBase91ShortString(): String {
return BASE91.encode(data)
}
fun getBase256ShortString(): String {
return BASE256.encode(data)
}
/**
* 从时间戳型 UUID 中提取 MAC 地址并允许自定义分隔符
* 提取 UUID V1 中的 MAC 地址
*
* @param spec 分隔符字符默认为 ":"
* @param spec 分隔符默认为冒号
* @return MAC 地址字符串
*/
fun extractMACFromUUID(spec: String?): String {
var spec = spec
if (spec == null) {
spec = ":"
fun getMac(spec: String = ":"): String {
if (version != 1) {
throw IllegalArgumentException("UUID version is $version not v1 : not supported ")
}
val leastSigBits = uuid.leastSignificantBits
val macLong = leastSigBits and 0xFFFFFFFFFFFFL
val macBytes = ByteArray(6)
for (i in 0..5) {
@ -230,34 +690,4 @@ class UUID : Serializable {
}
return mac.toString()
}
/**
* 返回此 UUID 的字符串表示包含版本信息和时间戳如果是版本1
*
* @return UUID 的详细字符串表示
*/
override fun toString(): String {
return "UUID(uuid=${getString()},version=${uuid.version()})"
}
/**
* 判断两个 UUID 是否相等
*
* @param other 比较对象
* @return 如果相等返回 true否则返回 false
*/
override fun equals(other: Any?): Boolean {
return uuid == other
}
/**
* 计算此 UUID 的哈希码
*
* @return 哈希码值
*/
override fun hashCode(): Int {
return uuid.hashCode()
}
}

View File

@ -16,7 +16,7 @@
# ProjectName mingli-utils
# ModuleName mingli-utils.main
# CurrentFile org.springframework.boot.autoconfigure.AutoConfiguration.imports
# LastUpdate 2025-09-09 08:37:33
# LastUpdate 2025-09-15 22:32:50
# UpdateUser MingLiPro
#