Compare commits

...

16 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
48 changed files with 4575 additions and 922 deletions

1
.gitignore vendored
View File

@ -45,3 +45,4 @@ log
node_modules node_modules
*lock* *lock*
.kotlin .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 * ProjectName mingli-utils
* ModuleName mingli-utils * ModuleName mingli-utils
* CurrentFile build.gradle.kts * CurrentFile build.gradle.kts
* LastUpdate 2025-09-15 22:22:00 * LastUpdate 2025-09-21 15:36:59
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -26,8 +26,9 @@ import java.time.format.DateTimeFormatter
plugins { plugins {
idea idea
java java
id("java-library") signing
id("maven-publish") `java-library`
`maven-publish`
kotlin("jvm") version "2.2.20" kotlin("jvm") version "2.2.20"
id("org.jetbrains.dokka") version "2.0.0" id("org.jetbrains.dokka") version "2.0.0"
} }
@ -64,22 +65,21 @@ java {
} }
dependencies { 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("org.springframework.boot:spring-boot-starter:2.7.14")
compileOnly("com.fasterxml.jackson.core:jackson-databind:2.19.2") compileOnly("com.fasterxml.jackson.core:jackson-databind:2.19.2")
compileOnly("com.google.code.gson:gson:2.13.1") compileOnly("com.google.code.gson:gson:2.13.1")
compileOnly("org.mybatis:mybatis:3.5.19") compileOnly("org.mybatis:mybatis:3.5.19")
compileOnly("com.alibaba.fastjson2:fastjson2:2.0.58") 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("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")
} }
@ -96,10 +96,12 @@ tasks.withType<JavaExec>().configureEach {
tasks.withType<org.gradle.jvm.tasks.Jar> { tasks.withType<org.gradle.jvm.tasks.Jar> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from("LICENSE") { into("META-INF") }
from("NOTICE") { into("META-INF") }
manifest { manifest {
attributes( attributes(
mapOf( mapOf(
"Main-Class" to "com.mingliqiye.utils.Main", "Main-Class" to "com.mingliqiye.utils.main.Main",
"Specification-Title" to ARTIFACTID, "Specification-Title" to ARTIFACTID,
"Specification-Version" to VERSIONS, "Specification-Version" to VERSIONS,
"Specification-Vendor" to "minglipro", "Specification-Vendor" to "minglipro",
@ -130,17 +132,16 @@ repositories {
} }
mavenCentral() mavenCentral()
} }
tasks.register<Jar>("javaDocJar") { tasks.register<Jar>("javaDocJar") {
group = "build" group = "build"
archiveClassifier.set("javadoc") archiveClassifier.set("javadoc")
dependsOn(tasks.dokkaJavadoc) dependsOn("dokkaJavadoc")
from(buildDir.resolve("dokka/javadoc")) from(buildDir.resolve("dokka/javadoc"))
} }
tasks.register<Jar>("kotlinDocJar") { tasks.register<Jar>("kotlinDocJar") {
group = "build" group = "build"
archiveClassifier.set("kotlindoc") archiveClassifier.set("kotlindoc")
dependsOn(tasks.dokkaHtml) dependsOn("dokkaHtml")
from(buildDir.resolve("dokka/html")) from(buildDir.resolve("dokka/html"))
} }
publishing { publishing {
@ -149,6 +150,10 @@ publishing {
name = "MavenRepositoryRaw" name = "MavenRepositoryRaw"
url = uri("C:/data/git/maven-repository-raw") url = uri("C:/data/git/maven-repository-raw")
} }
maven {
name = "OSSRepository"
url = uri("C:/data/git/maven-repository-raw-utils")
}
} }
publications { publications {
create<MavenPublication>("mavenJava") { create<MavenPublication>("mavenJava") {
@ -157,8 +162,34 @@ publishing {
artifact(tasks.named("kotlinDocJar")) artifact(tasks.named("kotlinDocJar"))
artifactId = ARTIFACTID artifactId = ARTIFACTID
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8)) 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 { tasks.build {

View File

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

View File

@ -16,12 +16,16 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.jdk8 * ModuleName mingli-utils.jdk8
* CurrentFile build.gradle.kts * CurrentFile build.gradle.kts
* LastUpdate 2025-09-15 22:32:50 * LastUpdate 2025-09-21 15:39:12
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
plugins { plugins {
id("java-library") id("java-library")
id("maven-publish") 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 GROUPSID = project.properties["GROUPSID"] as String
val VERSIONS = project.properties["VERSIONS"] as String val VERSIONS = project.properties["VERSIONS"] as String
@ -39,15 +43,44 @@ publishing {
name = "MavenRepositoryRaw" name = "MavenRepositoryRaw"
url = uri("C:/data/git/maven-repository-raw") url = uri("C:/data/git/maven-repository-raw")
} }
maven {
name = "OSSRepository"
url = uri("C:/data/git/maven-repository-raw-utils")
}
} }
publications { publications {
create<MavenPublication>("mavenJava") { create<MavenPublication>("mavenJava") {
from(components["java"]) from(components["java"])
artifactId = "$ARTIFACTID-win-jdk8" artifactId = "$ARTIFACTID-win-jdk8"
groupId = GROUPSID java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))
version = VERSIONS 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)) 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,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 * ProjectName mingli-utils
* ModuleName mingli-utils * ModuleName mingli-utils
* CurrentFile settings.gradle.kts * CurrentFile settings.gradle.kts
* LastUpdate 2025-09-15 22:32:50 * LastUpdate 2025-09-16 12:32:52
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */

View File

@ -15,29 +15,20 @@
* *
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile ComponentBean.kt * CurrentFile StreamEmptyException.java
* LastUpdate 2025-09-15 22:32:50 * LastUpdate 2025-09-20 13:24:07
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
package com.mingliqiye.utils.bean.annotation package com.mingliqiye.utils.stream;
import kotlin.annotation.AnnotationRetention.RUNTIME public class StreamEmptyException extends java.lang.RuntimeException {
import kotlin.annotation.AnnotationTarget.CLASS
import kotlin.annotation.AnnotationTarget.FIELD
/** public StreamEmptyException(String message) {
* 组件bean注解 super(message);
* @author MingLiPro }
*/
@Retention(RUNTIME)
@Target(CLASS)
annotation class ComponentBean(val value: String = "")
/** public StreamEmptyException(String message, Throwable cause) {
* 注入bean注解 super(message, cause);
* @author MingLiPro }
*/ }
@Retention(RUNTIME)
@Target(FIELD)
annotation class InjectBean(val value: String = "")

File diff suppressed because it is too large Load Diff

View File

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

@ -16,13 +16,15 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile ByteUtils.kt * CurrentFile ByteUtils.kt
* LastUpdate 2025-09-15 17:26:34 * LastUpdate 2025-09-20 11:49:05
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("ByteUtils") @file:JvmName("ByteUtils")
package com.mingliqiye.utils.bytes package com.mingliqiye.utils.bytes
import com.mingliqiye.utils.stream.SuperStream
const val ESC_ASC: Byte = 0x10 const val ESC_ASC: Byte = 0x10
const val ESC_DESC: Byte = 0x1B const val ESC_DESC: Byte = 0x1B
const val ESC_NONE: Byte = 0x00 const val ESC_NONE: Byte = 0x00
@ -39,8 +41,20 @@ const val ESC_RESERVED: Byte = 0x06
* @return 包含每个字节对应十六进制字符串的列表 * @return 包含每个字节对应十六进制字符串的列表
*/ */
fun ByteArray.getByteArrayString(): MutableList<String> { fun ByteArray.getByteArrayString(): MutableList<String> {
return this.toList().stream() return this.toList().stream().map { a -> String.format("0X%02X", a!!.toInt() and 0xFF) }
.map { a -> String.format("0X%02X", a!!.toInt() and 0xFF) } .collect(SuperStream.toList()) as MutableList<String>
.collect(com.mingliqiye.utils.stream.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 * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile CloneUtils.kt * CurrentFile CloneUtils.kt
* LastUpdate 2025-09-15 09:30:37 * LastUpdate 2025-09-20 14:01:29
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("CloneUtils") @file:JvmName("CloneUtils")
@ -38,8 +38,7 @@ inline fun <reified T> T.deepJsonClone(jsonApi: JsonApi): T {
} catch (e: Exception) { } catch (e: Exception) {
throw JsonException( throw JsonException(
"Failed to deep clone object using JSON", "Failed to deep clone object using JSON", e
e
) )
} }
} }

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile Collection.kt * CurrentFile Collection.kt
* LastUpdate 2025-09-15 17:26:00 * LastUpdate 2025-09-21 14:36:57
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -24,22 +24,48 @@
package com.mingliqiye.utils.collection package com.mingliqiye.utils.collection
import com.mingliqiye.utils.stream.SuperStream
import java.util.* import java.util.*
import java.util.stream.Collectors import java.util.stream.Collectors
/**
* 将当前集合转换为数组
*
* @param T 集合元素类型
* @return 转换后的数组
*/
inline fun <reified T> Collection<T>.toArray(): Array<T> { inline fun <reified T> Collection<T>.toArray(): Array<T> {
return arrayOf(*this.toTypedArray()) 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> { inline fun <reified T, V> Collection<T>.toMap(noinline v: (T) -> V): Map<T, V> {
return this.stream().collect( return this.stream().collect(
com.mingliqiye.utils.stream.toMapValueThis( SuperStream.toMap(
v 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> { inline fun <reified T, V, K> Collection<T>.toMap(noinline k: (T) -> K, noinline v: (T) -> V): Map<K, V> {
return this.stream().collect( return this.stream().collect(
Collectors.toMap( Collectors.toMap(
@ -48,11 +74,24 @@ inline fun <reified T, V, K> Collection<T>.toMap(noinline k: (T) -> K, noinline
) )
} }
/**
* 将数组转换为 Set
*
* @param T 数组元素类型
* @param array 输入数组
* @return 转换后的 Set
*/
fun <T> toSet(array: Array<T>): Set<T> { fun <T> toSet(array: Array<T>): Set<T> {
return array.toSet() return array.toSet()
} }
/**
* 获取集合中的第一个元素如果集合为空则返回 null
*
* @param T 集合元素类型
* @return 第一个元素或 null
*/
inline fun <reified T> Collection<T>.getFirst(): T? { inline fun <reified T> Collection<T>.getFirst(): T? {
if (this.isEmpty()) { if (this.isEmpty()) {
return null return null
@ -63,6 +102,12 @@ inline fun <reified T> Collection<T>.getFirst(): T? {
return this.iterator().next() return this.iterator().next()
} }
/**
* 获取数组中的第一个元素如果数组为空则返回 null
*
* @param T 数组元素类型
* @return 第一个元素或 null
*/
inline fun <reified T> Array<T>.getFirst(): T? { inline fun <reified T> Array<T>.getFirst(): T? {
if (this.isEmpty()) { if (this.isEmpty()) {
return null return null
@ -70,6 +115,12 @@ inline fun <reified T> Array<T>.getFirst(): T? {
return this.first() return this.first()
} }
/**
* 获取集合中的最后一个元素如果集合为空则返回 null
*
* @param T 集合元素类型
* @return 最后一个元素或 null
*/
inline fun <reified T> Collection<T>.getLast(): T? { inline fun <reified T> Collection<T>.getLast(): T? {
if (this.isEmpty()) { if (this.isEmpty()) {
return null return null
@ -84,6 +135,12 @@ inline fun <reified T> Collection<T>.getLast(): T? {
return lastElement return lastElement
} }
/**
* 获取数组中的最后一个元素如果数组为空则返回 null
*
* @param T 数组元素类型
* @return 最后一个元素或 null
*/
inline fun <reified T> Array<T>.getLast(): T? { inline fun <reified T> Array<T>.getLast(): T? {
if (this.isEmpty()) { if (this.isEmpty()) {
return null 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( inline fun <reified T> Collection<T>.getOrDefault(
index: Int, defaultValue: T index: Int, defaultValue: T
): T { ): T {
@ -111,54 +176,135 @@ inline fun <reified T> Collection<T>.getOrDefault(
return defaultValue return defaultValue
} }
/**
* 创建一个新的 ArrayList 实例
*
* @param T 元素类型
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(): ArrayList<T> { fun <T> newArrayList(): ArrayList<T> {
return ArrayList() return ArrayList()
} }
/**
* 创建一个指定初始容量的新 ArrayList 实例
*
* @param T 元素类型
* @param size 初始容量大小
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(size: Int): ArrayList<T> { fun <T> newArrayList(size: Int): ArrayList<T> {
return ArrayList() return ArrayList()
} }
/**
* 使用可变参数创建一个新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(vararg elements: T): ArrayList<T> { fun <T> newArrayList(vararg elements: T): ArrayList<T> {
return newArrayList(elements.asList()) return newArrayList(elements.asList())
} }
/**
* 将当前集合转换为新的 ArrayList 实例
*
* @param T 元素类型
* @return 新创建的 ArrayList
*/
fun <T> Collection<T>.newArrayLists(): ArrayList<T> { fun <T> Collection<T>.newArrayLists(): ArrayList<T> {
return newArrayList(this) return newArrayList(this)
} }
/**
* 将数组转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入数组
* @return 新创建的 ArrayList
*/
fun <T> newArrayLists(elements: Array<T>): ArrayList<T> { fun <T> newArrayLists(elements: Array<T>): ArrayList<T> {
return newArrayList(elements.asList()) return newArrayList(elements.asList())
} }
/**
* 将集合转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(elements: Collection<T>): ArrayList<T> { fun <T> newArrayList(elements: Collection<T>): ArrayList<T> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* Iterable 转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(elements: Iterable<T>): ArrayList<T> { fun <T> newArrayList(elements: Iterable<T>): ArrayList<T> {
return newArrayList(elements.toList()) return newArrayList(elements.toList())
} }
/**
* Sequence 转换为新的 ArrayList 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 ArrayList
*/
fun <T> newArrayList(elements: Sequence<T>): ArrayList<T> { fun <T> newArrayList(elements: Sequence<T>): ArrayList<T> {
return newArrayList(elements.toList()) return newArrayList(elements.toList())
} }
/**
* 创建一个新的 LinkedList 实例
*
* @param T 元素类型
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(): LinkedList<T> { fun <T> newLinkedList(): LinkedList<T> {
return LinkedList() return LinkedList()
} }
/**
* 使用可变参数创建一个新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(vararg elements: T): LinkedList<T> { fun <T> newLinkedList(vararg elements: T): LinkedList<T> {
val list = newLinkedList<T>() val list = newLinkedList<T>()
list.addAll(elements.asList()) list.addAll(elements.asList())
return list return list
} }
/**
* 将集合转换为新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(elements: Collection<T>): LinkedList<T> { fun <T> newLinkedList(elements: Collection<T>): LinkedList<T> {
val list = newLinkedList<T>() val list = newLinkedList<T>()
list.addAll(elements) list.addAll(elements)
return list return list
} }
/**
* Iterable 转换为新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(elements: Iterable<T>): LinkedList<T> { fun <T> newLinkedList(elements: Iterable<T>): LinkedList<T> {
val list = newLinkedList<T>() val list = newLinkedList<T>()
for (element in elements) { for (element in elements) {
@ -167,26 +313,60 @@ fun <T> newLinkedList(elements: Iterable<T>): LinkedList<T> {
return list return list
} }
/**
* Sequence 转换为新的 LinkedList 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 LinkedList
*/
fun <T> newLinkedList(elements: Sequence<T>): LinkedList<T> { fun <T> newLinkedList(elements: Sequence<T>): LinkedList<T> {
return newLinkedList(elements.toList()) return newLinkedList(elements.toList())
} }
/**
* 创建一个新的 Vector 实例
*
* @param T 元素类型
* @return 新创建的 Vector
*/
fun <T> newVector(): Vector<T> { fun <T> newVector(): Vector<T> {
return Vector() return Vector()
} }
/**
* 使用可变参数创建一个新的 Vector 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 Vector
*/
fun <T> newVector(vararg elements: T): Vector<T> { fun <T> newVector(vararg elements: T): Vector<T> {
val vector = newVector<T>() val vector = newVector<T>()
vector.addAll(elements.asList()) vector.addAll(elements.asList())
return vector return vector
} }
/**
* 将集合转换为新的 Vector 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 Vector
*/
fun <T> newVector(elements: Collection<T>): Vector<T> { fun <T> newVector(elements: Collection<T>): Vector<T> {
val vector = newVector<T>() val vector = newVector<T>()
vector.addAll(elements) vector.addAll(elements)
return vector return vector
} }
/**
* Iterable 转换为新的 Vector 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 Vector
*/
fun <T> newVector(elements: Iterable<T>): Vector<T> { fun <T> newVector(elements: Iterable<T>): Vector<T> {
val vector = newVector<T>() val vector = newVector<T>()
for (element in elements) { for (element in elements) {
@ -195,31 +375,72 @@ fun <T> newVector(elements: Iterable<T>): Vector<T> {
return vector return vector
} }
/**
* Sequence 转换为新的 Vector 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 Vector
*/
fun <T> newVector(elements: Sequence<T>): Vector<T> { fun <T> newVector(elements: Sequence<T>): Vector<T> {
return newVector(elements.toList()) return newVector(elements.toList())
} }
/**
* 创建一个新的 HashSet 实例
*
* @param T 元素类型
* @return 新创建的 HashSet
*/
fun <T> newHashSet(): HashSet<T> { fun <T> newHashSet(): HashSet<T> {
return HashSet() return HashSet()
} }
/**
* 创建一个指定初始容量的新 HashSet 实例
*
* @param T 元素类型
* @param size 初始容量大小
* @return 新创建的 HashSet
*/
fun <T> newHashSet(size: Int): HashSet<T> { fun <T> newHashSet(size: Int): HashSet<T> {
return HashSet(size) return HashSet(size)
} }
/**
* 使用可变参数创建一个新的 HashSet 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 HashSet
*/
fun <T> newHashSet(vararg elements: T): HashSet<T> { fun <T> newHashSet(vararg elements: T): HashSet<T> {
val set = newHashSet<T>() val set = newHashSet<T>()
set.addAll(elements.asList()) set.addAll(elements.asList())
return set return set
} }
/**
* 将集合转换为新的 HashSet 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 HashSet
*/
fun <T> newHashSet(elements: Collection<T>): HashSet<T> { fun <T> newHashSet(elements: Collection<T>): HashSet<T> {
val set = newHashSet<T>() val set = newHashSet<T>()
set.addAll(elements) set.addAll(elements)
return set return set
} }
/**
* Iterable 转换为新的 HashSet 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 HashSet
*/
fun <T> newHashSet(elements: Iterable<T>): HashSet<T> { fun <T> newHashSet(elements: Iterable<T>): HashSet<T> {
val set = newHashSet<T>() val set = newHashSet<T>()
for (element in elements) { for (element in elements) {
@ -228,30 +449,71 @@ fun <T> newHashSet(elements: Iterable<T>): HashSet<T> {
return set return set
} }
/**
* Sequence 转换为新的 HashSet 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 HashSet
*/
fun <T> newHashSet(elements: Sequence<T>): HashSet<T> { fun <T> newHashSet(elements: Sequence<T>): HashSet<T> {
return newHashSet(elements.toSet()) return newHashSet(elements.toSet())
} }
/**
* 创建一个新的 LinkedHashSet 实例
*
* @param T 元素类型
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(): LinkedHashSet<T> { fun <T> newLinkedHashSet(): LinkedHashSet<T> {
return LinkedHashSet() return LinkedHashSet()
} }
/**
* 创建一个指定初始容量的新 LinkedHashSet 实例
*
* @param T 元素类型
* @param size 初始容量大小
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(size: Int): LinkedHashSet<T> { fun <T> newLinkedHashSet(size: Int): LinkedHashSet<T> {
return LinkedHashSet(size) return LinkedHashSet(size)
} }
/**
* 使用可变参数创建一个新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(vararg elements: T): LinkedHashSet<T> { fun <T> newLinkedHashSet(vararg elements: T): LinkedHashSet<T> {
val set = newLinkedHashSet<T>() val set = newLinkedHashSet<T>()
set.addAll(elements.asList()) set.addAll(elements.asList())
return set return set
} }
/**
* 将集合转换为新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(elements: Collection<T>): LinkedHashSet<T> { fun <T> newLinkedHashSet(elements: Collection<T>): LinkedHashSet<T> {
val set = newLinkedHashSet<T>() val set = newLinkedHashSet<T>()
set.addAll(elements) set.addAll(elements)
return set return set
} }
/**
* Iterable 转换为新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 输入 Iterable
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(elements: Iterable<T>): LinkedHashSet<T> { fun <T> newLinkedHashSet(elements: Iterable<T>): LinkedHashSet<T> {
val set = newLinkedHashSet<T>() val set = newLinkedHashSet<T>()
for (element in elements) { for (element in elements) {
@ -260,26 +522,60 @@ fun <T> newLinkedHashSet(elements: Iterable<T>): LinkedHashSet<T> {
return set return set
} }
/**
* Sequence 转换为新的 LinkedHashSet 实例
*
* @param T 元素类型
* @param elements 输入 Sequence
* @return 新创建的 LinkedHashSet
*/
fun <T> newLinkedHashSet(elements: Sequence<T>): LinkedHashSet<T> { fun <T> newLinkedHashSet(elements: Sequence<T>): LinkedHashSet<T> {
return newLinkedHashSet(elements.toSet()) return newLinkedHashSet(elements.toSet())
} }
/**
* 创建一个新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(): TreeSet<T> { fun <T : Comparable<T>> newTreeSet(): TreeSet<T> {
return TreeSet() return TreeSet()
} }
/**
* 使用可变参数创建一个新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 可变参数列表
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(vararg elements: T): TreeSet<T> { fun <T : Comparable<T>> newTreeSet(vararg elements: T): TreeSet<T> {
val set = newTreeSet<T>() val set = newTreeSet<T>()
set.addAll(elements.asList()) set.addAll(elements.asList())
return set return set
} }
/**
* 将集合转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 输入集合
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(elements: Collection<T>): TreeSet<T> { fun <T : Comparable<T>> newTreeSet(elements: Collection<T>): TreeSet<T> {
val set = newTreeSet<T>() val set = newTreeSet<T>()
set.addAll(elements) set.addAll(elements)
return set return set
} }
/**
* Iterable 转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 输入 Iterable
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(elements: Iterable<T>): TreeSet<T> { fun <T : Comparable<T>> newTreeSet(elements: Iterable<T>): TreeSet<T> {
val set = newTreeSet<T>() val set = newTreeSet<T>()
for (element in elements) { for (element in elements) {
@ -288,65 +584,160 @@ fun <T : Comparable<T>> newTreeSet(elements: Iterable<T>): TreeSet<T> {
return set return set
} }
/**
* Sequence 转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @param elements 输入 Sequence
* @return 新创建的 TreeSet
*/
fun <T : Comparable<T>> newTreeSet(elements: Sequence<T>): TreeSet<T> { fun <T : Comparable<T>> newTreeSet(elements: Sequence<T>): TreeSet<T> {
return newTreeSet(elements.toSet()) return newTreeSet(elements.toSet())
} }
/**
* 将字节数组转换为 ArrayList
*
* @param elements 输入字节数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: ByteArray): ArrayList<Byte> { fun newArrayLists(elements: ByteArray): ArrayList<Byte> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 将短整型数组转换为 ArrayList
*
* @param elements 输入短整型数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: ShortArray): ArrayList<Short> { fun newArrayLists(elements: ShortArray): ArrayList<Short> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 将整型数组转换为 ArrayList
*
* @param elements 输入整型数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: IntArray): ArrayList<Int> { fun newArrayLists(elements: IntArray): ArrayList<Int> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 将长整型数组转换为 ArrayList
*
* @param elements 输入长整型数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: LongArray): ArrayList<Long> { fun newArrayLists(elements: LongArray): ArrayList<Long> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 将浮点数组转换为 ArrayList
*
* @param elements 输入浮点数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: FloatArray): ArrayList<Float> { fun newArrayLists(elements: FloatArray): ArrayList<Float> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 将双精度浮点数组转换为 ArrayList
*
* @param elements 输入双精度浮点数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: DoubleArray): ArrayList<Double> { fun newArrayLists(elements: DoubleArray): ArrayList<Double> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 将布尔数组转换为 ArrayList
*
* @param elements 输入布尔数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: BooleanArray): ArrayList<Boolean> { fun newArrayLists(elements: BooleanArray): ArrayList<Boolean> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 将字符数组转换为 ArrayList
*
* @param elements 输入字符数组
* @return 转换后的 ArrayList
*/
fun newArrayLists(elements: CharArray): ArrayList<Char> { fun newArrayLists(elements: CharArray): ArrayList<Char> {
return ArrayList(elements.toList()) return ArrayList(elements.toList())
} }
/**
* 创建一个新的 CopyOnWriteArrayList 实例
*
* @param T 元素类型
* @return 新创建的 CopyOnWriteArrayList
*/
fun <T> newCopyOnWriteArrayList(): java.util.concurrent.CopyOnWriteArrayList<T> { fun <T> newCopyOnWriteArrayList(): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList() return java.util.concurrent.CopyOnWriteArrayList()
} }
/**
* 使用可变参数创建一个新的 CopyOnWriteArrayList 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 CopyOnWriteArrayList
*/
fun <T> newCopyOnWriteArrayList(vararg elements: T): java.util.concurrent.CopyOnWriteArrayList<T> { fun <T> newCopyOnWriteArrayList(vararg elements: T): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList(elements.asList()) 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> { fun <T> newCopyOnWriteArrayList(elements: Collection<T>): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList(elements) return java.util.concurrent.CopyOnWriteArrayList(elements)
} }
/**
* 创建一个新的 Stack 实例
*
* @param T 元素类型
* @return 新创建的 Stack
*/
fun <T> newStack(): Stack<T> { fun <T> newStack(): Stack<T> {
return Stack() return Stack()
} }
/**
* 使用可变参数创建一个新的 Stack 实例
*
* @param T 元素类型
* @param elements 可变参数列表
* @return 新创建的 Stack
*/
fun <T> newStack(vararg elements: T): Stack<T> { fun <T> newStack(vararg elements: T): Stack<T> {
val stack = newStack<T>() val stack = newStack<T>()
stack.addAll(elements.asList()) stack.addAll(elements.asList())
return stack return stack
} }
/**
* 将集合转换为新的 Stack 实例
*
* @param T 元素类型
* @param elements 输入集合
* @return 新创建的 Stack
*/
fun <T> newStack(elements: Collection<T>): Stack<T> { fun <T> newStack(elements: Collection<T>): Stack<T> {
val stack = newStack<T>() val stack = newStack<T>()
stack.addAll(elements) 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> { fun <T> newTreeSet(comparator: Comparator<T>): TreeSet<T> {
return TreeSet(comparator) return TreeSet(comparator)
} }
/**
* 使用可变参数创建一个新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 可变参数列表
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, vararg elements: T): TreeSet<T> { fun <T> newTreeSet(comparator: Comparator<T>, vararg elements: T): TreeSet<T> {
val set = newTreeSet(comparator) val set = newTreeSet(comparator)
set.addAll(elements.asList()) set.addAll(elements.asList())
return set return set
} }
/**
* 将集合转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 输入集合
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, elements: Collection<T>): TreeSet<T> { fun <T> newTreeSet(comparator: Comparator<T>, elements: Collection<T>): TreeSet<T> {
val set = newTreeSet(comparator) val set = newTreeSet(comparator)
set.addAll(elements) set.addAll(elements)
return set return set
} }
/**
* Iterable 转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 输入 Iterable
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, elements: Iterable<T>): TreeSet<T> { fun <T> newTreeSet(comparator: Comparator<T>, elements: Iterable<T>): TreeSet<T> {
val set = newTreeSet(comparator) val set = newTreeSet(comparator)
for (element in elements) { for (element in elements) {
@ -378,32 +800,71 @@ fun <T> newTreeSet(comparator: Comparator<T>, elements: Iterable<T>): TreeSet<T>
return set return set
} }
/**
* Sequence 转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @param elements 输入 Sequence
* @return 新创建的 TreeSet
*/
fun <T> newTreeSet(comparator: Comparator<T>, elements: Sequence<T>): TreeSet<T> { fun <T> newTreeSet(comparator: Comparator<T>, elements: Sequence<T>): TreeSet<T> {
return newTreeSet(comparator, elements.toSet()) return newTreeSet(comparator, elements.toSet())
} }
/**
* 将当前集合转换为新的 CopyOnWriteArrayList 实例
*
* @param T 元素类型
* @return 新创建的 CopyOnWriteArrayList
*/
fun <T> Collection<T>.newCopyOnWriteArrayLists(): java.util.concurrent.CopyOnWriteArrayList<T> { fun <T> Collection<T>.newCopyOnWriteArrayLists(): java.util.concurrent.CopyOnWriteArrayList<T> {
return java.util.concurrent.CopyOnWriteArrayList(this) return java.util.concurrent.CopyOnWriteArrayList(this)
} }
/**
* 将当前集合转换为新的 Stack 实例
*
* @param T 元素类型
* @return 新创建的 Stack
*/
fun <T> Collection<T>.newStacks(): Stack<T> { fun <T> Collection<T>.newStacks(): Stack<T> {
val stack = Stack<T>() val stack = Stack<T>()
stack.addAll(this) stack.addAll(this)
return stack return stack
} }
/**
* 将当前集合转换为新的 TreeSet 实例
*
* @param T 元素类型必须实现 Comparable 接口
* @return 新创建的 TreeSet
*/
fun <T> Collection<T>.newTreeSets(): TreeSet<T> where T : Comparable<T> { fun <T> Collection<T>.newTreeSets(): TreeSet<T> where T : Comparable<T> {
val set = TreeSet<T>() val set = TreeSet<T>()
set.addAll(this) set.addAll(this)
return set return set
} }
/**
* 将当前集合转换为新的 TreeSet 实例并指定比较器
*
* @param T 元素类型
* @param comparator 用于排序的比较器
* @return 新创建的 TreeSet
*/
fun <T> Collection<T>.newTreeSets(comparator: Comparator<T>): TreeSet<T> { fun <T> Collection<T>.newTreeSets(comparator: Comparator<T>): TreeSet<T> {
val set = TreeSet(comparator) val set = TreeSet(comparator)
set.addAll(this) set.addAll(this)
return set return set
} }
/**
* Byte 类型的 List 转换为字节数组
*
* @param list 输入的 Byte 列表
* @return 转换后的字节数组
*/
fun toArray(list: List<Byte>): ByteArray { fun toArray(list: List<Byte>): ByteArray {
val arr = ByteArray(list.size) val arr = ByteArray(list.size)
for (i in list.indices) { 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 { fun toArray(list: List<Short>): ShortArray {
val arr = ShortArray(list.size) val arr = ShortArray(list.size)
for (i in list.indices) { for (i in list.indices) {
@ -421,6 +888,12 @@ fun toArray(list: List<Short>): ShortArray {
return arr return arr
} }
/**
* Int 类型的 List 转换为整型数组
*
* @param list 输入的 Int 列表
* @return 转换后的整型数组
*/
fun toArray(list: List<Int>): IntArray { fun toArray(list: List<Int>): IntArray {
val arr = IntArray(list.size) val arr = IntArray(list.size)
for (i in list.indices) { for (i in list.indices) {
@ -429,6 +902,12 @@ fun toArray(list: List<Int>): IntArray {
return arr return arr
} }
/**
* Long 类型的 List 转换为长整型数组
*
* @param list 输入的 Long 列表
* @return 转换后的长整型数组
*/
fun toArray(list: List<Long>): LongArray { fun toArray(list: List<Long>): LongArray {
val arr = LongArray(list.size) val arr = LongArray(list.size)
for (i in list.indices) { for (i in list.indices) {
@ -437,6 +916,12 @@ fun toArray(list: List<Long>): LongArray {
return arr return arr
} }
/**
* Float 类型的 List 转换为浮点数组
*
* @param list 输入的 Float 列表
* @return 转换后的浮点数组
*/
fun toArray(list: List<Float>): FloatArray { fun toArray(list: List<Float>): FloatArray {
val arr = FloatArray(list.size) val arr = FloatArray(list.size)
for (i in list.indices) { for (i in list.indices) {
@ -445,6 +930,12 @@ fun toArray(list: List<Float>): FloatArray {
return arr return arr
} }
/**
* Double 类型的 List 转换为双精度浮点数组
*
* @param list 输入的 Double 列表
* @return 转换后的双精度浮点数组
*/
fun toArray(list: List<Double>): DoubleArray { fun toArray(list: List<Double>): DoubleArray {
val arr = DoubleArray(list.size) val arr = DoubleArray(list.size)
for (i in list.indices) { for (i in list.indices) {
@ -453,6 +944,12 @@ fun toArray(list: List<Double>): DoubleArray {
return arr return arr
} }
/**
* Boolean 类型的 List 转换为布尔数组
*
* @param list 输入的 Boolean 列表
* @return 转换后的布尔数组
*/
fun toArray(list: List<Boolean>): BooleanArray { fun toArray(list: List<Boolean>): BooleanArray {
val arr = BooleanArray(list.size) val arr = BooleanArray(list.size)
for (i in list.indices) { for (i in list.indices) {
@ -461,6 +958,12 @@ fun toArray(list: List<Boolean>): BooleanArray {
return arr return arr
} }
/**
* Char 类型的 List 转换为字符数组
*
* @param list 输入的 Char 列表
* @return 转换后的字符数组
*/
fun toArray(list: List<Char>): CharArray { fun toArray(list: List<Char>): CharArray {
val arr = CharArray(list.size) val arr = CharArray(list.size)
for (i in list.indices) { for (i in list.indices) {
@ -468,3 +971,23 @@ fun toArray(list: List<Char>): CharArray {
} }
return arr 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 * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile IsChanged.kt * CurrentFile IsChanged.kt
* LastUpdate 2025-09-15 09:30:37 * LastUpdate 2025-09-19 20:17:07
* UpdateUser MingLiPro * 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,25 +16,22 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile HashUtils.kt * CurrentFile HashUtils.kt
* LastUpdate 2025-09-15 09:38:04 * LastUpdate 2025-09-19 20:24:33
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("HashUtils") @file:JvmName("HashUtils")
package com.mingliqiye.utils.hash 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.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.IOException import java.io.IOException
import java.security.MessageDigest import java.security.MessageDigest
import java.security.NoSuchAlgorithmException 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 对应的十六进制字符串 * @return 对应的十六进制字符串
*/ */
private fun bytesToHex(bytes: ByteArray): String { private fun bytesToHex(bytes: ByteArray): String {
val hexString = StringBuilder(2 * bytes.size) return BASE16.encode(bytes)
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()
} }
/** /**
@ -92,7 +81,7 @@ private fun bytesToHex(bytes: ByteArray): String {
* @return 加密后的 BCrypt 哈希字符串 * @return 加密后的 BCrypt 哈希字符串
*/ */
fun bcrypt(string: String): String { 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 * @return 如果匹配返回 true否则返回 false
*/ */
fun checkBcrypt(string: String, bcrypted: String): Boolean { 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

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile JsonTypeUtils.kt * CurrentFile JsonTypeUtils.kt
* LastUpdate 2025-09-15 22:04:54 * LastUpdate 2025-09-17 11:12:06
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("JsonTypeUtils") @file:JvmName("JsonTypeUtils")
@ -177,11 +177,11 @@ fun <K, V> MapType(keyType: Class<K>, valueType: Class<V>): JsonTypeReference<Ma
return null return null
} }
override fun equals(obj: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === obj) return true if (this === other) return true
if (obj !is ParameterizedType) return false if (other !is ParameterizedType) return false
val that = obj val that = other
return (Objects.equals( return (Objects.equals(
rawType, rawType,
that.rawType that.rawType

View File

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

View File

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

View File

@ -16,16 +16,20 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile Main.kt * CurrentFile Main.kt
* LastUpdate 2025-09-15 22:31:33 * LastUpdate 2025-09-20 13:22:11
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("Main") @file:JvmName("Main")
package com.mingliqiye.utils package com.mingliqiye.utils.main
import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration
import com.mingliqiye.utils.stream.SuperStream
fun main() { fun main() {
AutoConfiguration.printBanner() AutoConfiguration.printBanner()
val data = SuperStream.of(Array(0) { 1 })
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

@ -0,0 +1,42 @@
/*
* 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 QueryWrapper.kt
* LastUpdate 2025-09-20 14:21:44
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatisplus
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper
/**
* BaseMapperQuery接口扩展了BaseMapper提供了通用的查询包装器功能
*
* @param T 实体类类型
*/
interface BaseMapperQuery<T> : BaseMapper<T> {
/**
* 创建并返回一个新的QueryWrapper实例
*
* @return QueryWrapper<T> 返回类型化的查询包装器实例
*/
fun queryWrapper(): QueryWrapper<T> {
return QueryWrapper<T>()
}
}

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 * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile OsPath.kt * CurrentFile OsPath.kt
* LastUpdate 2025-09-15 08:59:15 * LastUpdate 2025-09-18 09:47:43
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -26,9 +26,8 @@ package com.mingliqiye.utils.path
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import java.nio.file.* import java.nio.file.Path
import java.util.* import java.nio.file.Paths
import java.util.function.Consumer
class OsPath private constructor(private val path: Path) : Path by path { 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("")) 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,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile RandomBytes.kt * CurrentFile RandomBytes.kt
* LastUpdate 2025-09-15 22:27:36 * LastUpdate 2025-09-16 17:42:26
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("RandomBytes") @file:JvmName("RandomBytes")
@ -82,9 +82,10 @@ fun randomByteNoHave(from: Byte, to: Byte): Byte {
} }
val secureRandom: SecureRandom by lazy { val secureRandom: SecureRandom by lazy {
SecureRandom() SecureRandom.getInstanceStrong()
} }
fun randomByteSecure(size: Int): ByteArray { fun randomByteSecure(size: Int): ByteArray {
val bytes = ByteArray(size) val bytes = ByteArray(size)
secureRandom.nextBytes(bytes) secureRandom.nextBytes(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

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile AesUtils.kt * CurrentFile AesUtils.kt
* LastUpdate 2025-09-15 22:32:50 * LastUpdate 2025-09-19 20:18:09
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -51,10 +51,4 @@ fun encryptAesGcmNoPadding(src: String, key: String, iv: ByteArray): ByteArray {
return encryptAesGcmNoPadding(src.toByteArray(), key.toByteArray(), iv) return encryptAesGcmNoPadding(src.toByteArray(), key.toByteArray(), iv)
} }
fun main() {
val iv = getRandomBytes(16)
println(encryptAesGcmNoPadding("mingliqiye", "key", iv))
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile AutoConfiguration.kt * CurrentFile AutoConfiguration.kt
* LastUpdate 2025-09-15 22:20:25 * LastUpdate 2025-09-20 10:47:00
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -34,7 +34,7 @@ import java.io.IOException
@org.springframework.boot.autoconfigure.AutoConfiguration @org.springframework.boot.autoconfigure.AutoConfiguration
@ComponentScan( @ComponentScan(
"com.mingliqiye.utils.bean.springboot", "com.mingliqiye.utils.springboot.bean",
"com.mingliqiye.utils.springboot.converters" "com.mingliqiye.utils.springboot.converters"
) )
open class AutoConfiguration { open class AutoConfiguration {
@ -54,10 +54,7 @@ open class AutoConfiguration {
fun printBanner() { fun printBanner() {
val bannerBuilder = StringBuilder(banner) val bannerBuilder = StringBuilder(banner)
try { try {
val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data") val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data") ?: return
if (inputStream == null) {
return
}
inputStream.use { stream -> inputStream.use { stream ->
var readlen: Int var readlen: Int
val buffer = ByteArray(1024) val buffer = ByteArray(1024)

View File

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

View File

@ -1,122 +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 SuperStream.kt
* LastUpdate 2025-09-15 17:17:48
* UpdateUser MingLiPro
*/
@file:JvmName("Colls")
package com.mingliqiye.utils.stream
import java.util.stream.Collector
import java.util.stream.Collectors
import java.util.stream.Stream
class SuperStream<T> private constructor(val stream: Stream<T>) : Stream<T> by stream {
companion object {
@JvmStatic
fun <T> of(stream: Stream<T>): SuperStream<T> {
return SuperStream(stream)
}
@JvmStatic
fun <T> of(collection: Collection<T>): SuperStream<T> {
return SuperStream(collection.stream())
}
@JvmStatic
fun <T : Map<K, V>, K, V> of(map: T): SuperStream<Map.Entry<K, V>> {
return of(map.entries)
}
@JvmStatic
fun <T> of(vararg array: T): SuperStream<T> {
return of(array.toList())
}
@JvmStatic
fun <T> of(iterator: Iterator<T>): SuperStream<T> {
val data = ArrayList<T>(20)
while (iterator.hasNext()) {
data.add(iterator.next())
}
return of(data)
}
}
}
interface Gettable<T> {
fun get(): T
}
interface KeyGettable<T> : Gettable<T> {
fun getKey(): T
override fun get(): T {
return getKey()
}
}
interface IdGettable<T> : Gettable<T> {
fun getId(): T
override fun get(): T {
return getId()
}
}
fun <T> getThis(t: T): T {
return t
}
fun <T, U> toMapValueThis(valueMapper: java.util.function.Function<in T, out U>): Collector<T, *, Map<T, U>> {
return Collectors.toMap(
java.util.function.Function<T, T> { it },
valueMapper
) as Collector<T, *, Map<T, U>>
}
fun <T, K> toMap(keyMapper: java.util.function.Function<in T, out K>): Collector<T, *, Map<K, T>> {
return Collectors.toMap(
keyMapper,
java.util.function.Function<T, T> { it },
) as Collector<T, *, Map<K, T>>
}
fun <K> toMapGet(): Collector<Gettable<K>, *, Map<K, Gettable<K>>> {
return Collectors.toMap(
java.util.function.Function<Gettable<K>, K> { it.get() },
java.util.function.Function<Gettable<K>, Gettable<K>> { it },
) as Collector<Gettable<K>, *, Map<K, Gettable<K>>>
}
fun <K, V> toMap(): Collector<Map.Entry<K, V>, *, Map<K, V>> {
return Collectors.toMap(
{ entry: Map.Entry<K, V> -> entry.key },
{ entry: Map.Entry<K, V> -> entry.value }
) as Collector<Map.Entry<K, V>, *, Map<K, V>>
}
fun <T> toList(): Collector<T, *, List<T>> {
return Collectors.toList<T>()
}
fun <T> toSet(): Collector<T, *, Set<T>> {
return Collectors.toSet<T>()
}

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile StringUtils.kt * CurrentFile StringUtils.kt
* LastUpdate 2025-09-15 22:32:50 * LastUpdate 2025-09-18 09:26:41
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("StringUtils") @file:JvmName("StringUtils")
@ -28,14 +28,17 @@ import com.mingliqiye.utils.logger.mingLiLoggerFactory
val log = mingLiLoggerFactory.getLogger("StringUtils") val log = mingLiLoggerFactory.getLogger("StringUtils")
val NULLISH_STRINGS = setOf("null", "NaN", "undefined", "None", "none")
/** /**
* 判断`字符串`是否为空 * 判断`字符串`是否为空
* *
* @param str 待判断的字符串 * @param str 待判断的字符串
* @return `true`: `false`: 非空 * @return `true`: `false`: 非空
*/ */
fun isEmpty(str: String?): Boolean { @JvmName("isEmpty")
return str?.isEmpty() != null 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) return StringBuilder(this)
} }
/**
* 将字符串按照指定分隔符进行分割
* @param str 需要分割的字符串
* @param separator 分割符
* @return 分割后的字符串列表
*/
fun split(str: String, separator: String): List<String> { fun split(str: String, separator: String): List<String> {
return str.split(separator) 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,13 +16,14 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile SystemUtil.kt * CurrentFile SystemUtil.kt
* LastUpdate 2025-09-15 22:19:57 * LastUpdate 2025-09-16 17:36:11
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("SystemUtils") @file:JvmName("SystemUtils")
package com.mingliqiye.utils.system package com.mingliqiye.utils.system
import com.mingliqiye.utils.random.randomByteSecure
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import java.net.Inet4Address import java.net.Inet4Address
import java.net.InetAddress import java.net.InetAddress
@ -273,3 +274,104 @@ private fun getEnvVar(name: String): String? {
null 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 * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile DateTime.kt * CurrentFile DateTime.kt
* LastUpdate 2025-09-15 22:32:50 * LastUpdate 2025-09-17 19:06:39
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -46,8 +46,7 @@ import kotlin.time.Instant
* @author MingLiPro * @author MingLiPro
*/ */
class DateTimeOffset private constructor( class DateTimeOffset private constructor(
val offsetType: ChronoUnit, val offsetType: ChronoUnit, val offset: Long
val offset: Long
) { ) {
companion object { companion object {
@ -96,6 +95,8 @@ enum class Formatter(private val value: String) {
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS * 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/ */
STANDARD_DATETIME_MILLISECOUND7("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 * 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
@ -152,6 +153,7 @@ enum class Formatter(private val value: String) {
*/ */
COMPACT_DATETIME("yyyyMMddHHmmss"); COMPACT_DATETIME("yyyyMMddHHmmss");
private val len: Int = value.length private val len: Int = value.length
fun getLen(): Int { fun getLen(): Int {
@ -179,15 +181,11 @@ enum class Formatter(private val value: String) {
* @see Instant * @see Instant
*/ */
class DateTime private constructor( class DateTime private constructor(
private var localDateTime: LocalDateTime, private var localDateTime: LocalDateTime, private val zoneId: ZoneId = ZoneId.systemDefault()
private val zoneId: ZoneId = ZoneId.systemDefault()
) : Serializable { ) : Serializable {
companion object { companion object {
private val WIN_KERNEL_32_API: WinKernel32Api? = if ( private val WIN_KERNEL_32_API: WinKernel32Api? = if (javaVersionAsInteger == 8 && isWindows) {
javaVersionAsInteger == 8 &&
isWindows
) {
val log: Logger = mingLiLoggerFactory.getLogger("mingli-utils DateTime") val log: Logger = mingLiLoggerFactory.getLogger("mingli-utils DateTime")
val a = getWinKernel32Apis() val a = getWinKernel32Apis()
@ -219,9 +217,7 @@ class DateTime private constructor(
fun now(): DateTime { fun now(): DateTime {
if (WIN_KERNEL_32_API != null) { if (WIN_KERNEL_32_API != null) {
return DateTime( return DateTime(
WIN_KERNEL_32_API.getTime() WIN_KERNEL_32_API.getTime().atZone(ZoneId.systemDefault()).toLocalDateTime()
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
) )
} }
return DateTime(LocalDateTime.now()) return DateTime(LocalDateTime.now())
@ -273,9 +269,7 @@ class DateTime private constructor(
*/ */
@JvmStatic @JvmStatic
fun parse( fun parse(
timestr: String, timestr: String, formatter: String, fillZero: Boolean
formatter: String,
fillZero: Boolean
): DateTime { ): DateTime {
return DateTime( return DateTime(
LocalDateTime.parse( 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 实例 * 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例
* *
@ -295,9 +303,7 @@ class DateTime private constructor(
*/ */
@JvmStatic @JvmStatic
fun parse( fun parse(
timestr: String, timestr: String, formatter: Formatter, fillZero: Boolean
formatter: Formatter,
fillZero: Boolean
): DateTime { ): DateTime {
return parse(timestr, formatter.getValue(), fillZero) return parse(timestr, formatter.getValue(), fillZero)
} }
@ -343,18 +349,14 @@ class DateTime private constructor(
modifiedDstr += "." modifiedDstr += "."
} }
val sb = StringBuilder(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") sb.append("0")
} }
return sb.toString() return sb.toString()
} }
throw IllegalArgumentException( throw IllegalArgumentException(
String.format( String.format(
"Text: '%s' len %s < %s %s", "Text: '%s' len %s < %s %s", dstr, dstr.length, formats, formats.length
dstr,
dstr.length,
formats,
formats.length
) )
) )
} }
@ -384,11 +386,7 @@ class DateTime private constructor(
*/ */
@JvmStatic @JvmStatic
fun of( fun of(
year: Int, year: Int, month: Int, day: Int, hour: Int, minute: Int
month: Int,
day: Int,
hour: Int,
minute: Int
): DateTime { ): DateTime {
return DateTime(LocalDateTime.of(year, month, day, hour, minute)) return DateTime(LocalDateTime.of(year, month, day, hour, minute))
} }
@ -407,8 +405,7 @@ class DateTime private constructor(
// 2. 从纳秒时间戳创建 Instant // 2. 从纳秒时间戳创建 Instant
val instant = java.time.Instant.ofEpochSecond( 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 // 3. 转换为系统默认时区的 LocalDateTime
@ -428,12 +425,7 @@ class DateTime private constructor(
*/ */
@JvmStatic @JvmStatic
fun of( fun of(
year: Int, year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int
): DateTime { ): DateTime {
return DateTime( return DateTime(
LocalDateTime.of(year, month, day, hour, minute, second) LocalDateTime.of(year, month, day, hour, minute, second)
@ -454,13 +446,7 @@ class DateTime private constructor(
*/ */
@JvmStatic @JvmStatic
fun of( fun of(
year: Int, year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, nano: Int
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int,
nano: Int
): DateTime { ): DateTime {
return DateTime( return DateTime(
LocalDateTime.of(year, month, day, hour, minute, second, nano) LocalDateTime.of(year, month, day, hour, minute, second, nano)
@ -476,9 +462,14 @@ class DateTime private constructor(
@JvmStatic @JvmStatic
fun of(epochMilli: Long): DateTime { fun of(epochMilli: Long): DateTime {
return DateTime( return DateTime(
java.time.Instant.ofEpochMilli(epochMilli) java.time.Instant.ofEpochMilli(epochMilli).atZone(ZoneId.systemDefault()).toLocalDateTime()
.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 @JvmStatic
fun of(epochMilli: Long, zoneId: ZoneId): DateTime { fun of(epochMilli: Long, zoneId: ZoneId): DateTime {
return DateTime( return DateTime(
java.time.Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime(), java.time.Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime(), zoneId
zoneId
) )
} }
} }
@ -525,8 +515,7 @@ class DateTime private constructor(
fun add(dateTimeOffset: DateTimeOffset): DateTime { fun add(dateTimeOffset: DateTimeOffset): DateTime {
return DateTime( return DateTime(
this.localDateTime.plus( this.localDateTime.plus(
dateTimeOffset.offset, dateTimeOffset.offset, dateTimeOffset.offsetType
dateTimeOffset.offsetType
) )
) )
} }
@ -538,12 +527,7 @@ class DateTime private constructor(
* @return 返回修改后的 DateTime 实例 * @return 返回修改后的 DateTime 实例
*/ */
fun sub(dateTimeOffset: DateTimeOffset): DateTime { fun sub(dateTimeOffset: DateTimeOffset): DateTime {
return DateTime( return add(DateTimeOffset.of(-dateTimeOffset.offset, dateTimeOffset.offsetType))
this.localDateTime.plus(
-dateTimeOffset.offset,
dateTimeOffset.offsetType
)
)
} }
/** /**
@ -566,6 +550,10 @@ class DateTime private constructor(
return format(formatter.getValue()) return format(formatter.getValue())
} }
fun format(): String {
return format(Formatter.STANDARD_DATETIME_MILLISECOUND9.getValue(), true)
}
/** /**
* 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零 * 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零
* *
@ -602,10 +590,7 @@ class DateTime private constructor(
* @return 返回标准格式的时间字符串 * @return 返回标准格式的时间字符串
*/ */
override fun toString(): String { override fun toString(): String {
return String.format( return "DateTime(${format()})"
"DateTime(%s)",
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
)
} }
/** /**
@ -675,4 +660,34 @@ class DateTime private constructor(
fun getZoneId(): ZoneId { fun getZoneId(): ZoneId {
return 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,70 +16,195 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile UUID.kt * CurrentFile UUID.kt
* LastUpdate 2025-09-15 18:01:30 * LastUpdate 2025-09-19 20:22:27
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
package com.mingliqiye.utils.uuid 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.DateTime
import com.mingliqiye.utils.time.DateTimeOffset import com.mingliqiye.utils.time.DateTimeOffset
import java.io.Serializable import java.io.Serializable
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.security.MessageDigest
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.*
import java.util.UUID as JUUID import java.util.UUID as JUUID
/**
* UUID 类用于生成和操作不同版本的 UUID通用唯一标识符
* 支持 UUID 的序列化转换解析和时间/版本信息提取
*/
class UUID : Serializable { 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 { companion object {
/** /**
* 获取 UUIDV1 版本的随机实例 * 预期 UUID 字符串中连字符的位置数组
* @return 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 @JvmStatic
fun getV1(): UUID { 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 版本的随机实例 * 生成一个 UUID V4 版本使用加密安全的随机数
* @return UUID *
* @return UUID V4 实例
*/ */
@JvmStatic @JvmStatic
fun getV4(): UUID { 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 版本的随机实例 * 生成一个 UUID V3 版本基于命名空间和名称的 MD5 哈希值
* @return UUID *
* @param namepath 命名空间 UUID
* @param user 用户提供的字符串
* @return UUID V3 实例
*/ */
@JvmStatic @JvmStatic
fun getV4Fast(): UUID { fun getV3(namepath: UUID, user: String): UUID {
return UUID(UuidCreator.getRandomBasedFast()) 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 * 生成一个 UUID V5 版本基于命名空间和名称的 SHA-1 哈希值
* @param lsb 高位 8 字节的 Long *
* @param msb 低位 8 字节的 Long * @param namepath 命名空间 UUID
* @return UUID * @param user 用户提供的字符串
* @return UUID V5 实例
*/ */
@JvmStatic @JvmStatic
fun of(msb: Long, lsb: Long): UUID { fun getV5(namepath: UUID, user: String): UUID {
return UUID(msb, lsb) 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())
} }
/** /**
* 从字符串格式化 * 生成一个 UUID V6 版本使用时间戳和随机节点信息
* @param uuid 字符串 *
* @return UUID * @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 @JvmStatic
fun of(uuid: String): UUID { fun of(uuid: String): UUID {
@ -87,135 +212,470 @@ class UUID : Serializable {
} }
/** /**
* 从Java的UUID * 根据字节数组创建 UUID 实例
* @param uuid 字符串 *
* @return UUID * @param uuid UUID 字节数组
* @return UUID 实例
*/ */
@JvmStatic @JvmStatic
fun of(uuid: JUUID): UUID { fun of(uuid: ByteArray): UUID {
return UUID(uuid) return UUID(uuid)
} }
/** /**
* 从字节码转换到UUID * 根据高位和低位长整型创建 UUID 实例
* @param array 16字节 *
* @return UUID * @param msb 高位长整型
* @param lsb 低位长整型
* @return UUID 实例
*/ */
@JvmStatic @JvmStatic
fun of(array: ByteArray): UUID { fun of(msb: Long, lsb: Long): UUID {
return UUID(array) 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) * 获取最小 UUID所有字节为 0x00
this.uuid = JUUID(bb.getLong(), bb.getLong()) *
} * @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
}
}
/** /**
* 获取对应的字节码 * 构造函数根据字节数组初始化 UUID
* @return 字节码 *
* @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 { fun toBytes(): ByteArray {
val bb = ByteBuffer.wrap(ByteArray(16)) return data
bb.putLong(uuid.mostSignificantBits)
bb.putLong(uuid.leastSignificantBits)
return bb.array()
} }
/** /**
* 获取Java的UUID对象 * 返回 UUID 的高位长整型部分
* @return Java的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 { fun getUuid(): JUUID {
return uuid return JUUID(mostSigBits, leastSigBits)
} }
/** /**
* UUID 转换为字符串表示默认使用小写格式 * 返回 UUID 的版本号
* @param u 是否大写 *
* @return UUID 字符串 * @return 版本号
*/ */
fun getString(u: Boolean): String { fun getVersion(): Int {
return if (u) { return version
uuid.toString().uppercase(Locale.ROOT) }
} else {
uuid.toString() /**
* 计算 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 转换为字符串表示默认使用小写格式 * 提取 UUID V1 中的 MAC 地址默认使用冒号分隔符
*
* @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 地址默认使用冒号分隔符
* *
* @return MAC 地址字符串 * @return MAC 地址字符串
*/ */
fun extractMACFromUUID(): String { fun getMac(): String {
return extractMACFromUUID(null) 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 地址字符串 * @return MAC 地址字符串
*/ */
fun extractMACFromUUID(spec: String?): String { fun getMac(spec: String = ":"): String {
var spec = spec if (version != 1) {
if (spec == null) { throw IllegalArgumentException("UUID version is $version not v1 : not supported ")
spec = ":"
} }
val leastSigBits = uuid.leastSignificantBits
val macLong = leastSigBits and 0xFFFFFFFFFFFFL val macLong = leastSigBits and 0xFFFFFFFFFFFFL
val macBytes = ByteArray(6) val macBytes = ByteArray(6)
for (i in 0..5) { for (i in 0..5) {
@ -230,39 +690,4 @@ class UUID : Serializable {
} }
return mac.toString() 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 {
if (other is UUID) {
return uuid == other.uuid
} else if (other is JUUID) {
return uuid == other
}
return false
}
/**
* 计算此 UUID 的哈希码
*
* @return 哈希码值
*/
override fun hashCode(): Int {
return uuid.hashCode()
}
} }