generated from mingliqiye/lib-tem
	Compare commits
	
		
			19 Commits
		
	
	
		
			Auto-Relea
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1509597032 | |||
| ac92f62967 | |||
| 683aeb2c7f | |||
| 5eed682aa1 | |||
| 578f0a3e89 | |||
| 075dc2346a | |||
| 23548c0c3d | |||
| 4b187f3774 | |||
| c90c1d590b | |||
| 42a3302495 | |||
| 496c3e6248 | |||
| 9002f41c63 | |||
| 58806e85f1 | |||
| 0f5748d55d | |||
| 8f8ffc72db | |||
| fcd528a821 | |||
| 0450e87c13 | |||
| 541a8a82b4 | |||
| 7c3c13e28c | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -45,3 +45,4 @@ log | |||||||
| node_modules | node_modules | ||||||
| *lock* | *lock* | ||||||
| .kotlin | .kotlin | ||||||
|  | secret.gpg | ||||||
|  | |||||||
| @ -1,8 +0,0 @@ | |||||||
| { |  | ||||||
|   "$schema": "https://json.schemastore.org/prettierrc", |  | ||||||
|   "plugins": [ |  | ||||||
|     "prettier-plugin-java" |  | ||||||
|   ], |  | ||||||
|   "tabWidth": 4, |  | ||||||
|   "useTabs": true |  | ||||||
| } |  | ||||||
							
								
								
									
										25
									
								
								NOTICE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								NOTICE
									
									
									
									
									
										Normal 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. | ||||||
|  | 
 | ||||||
|  | ------------------------------------------------------------ | ||||||
| @ -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 11:20:04 |  * 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,23 +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("jakarta.annotation:jakarta.annotation-api:2.1.1") |  | ||||||
|     implementation("org.slf4j:slf4j-api:2.0.17") |  | ||||||
|     implementation("com.mingliqiye.utils.jna:WinKernel32Api:1.0.1") |  | ||||||
| 
 | 
 | ||||||
|  |     compileOnly("com.baomidou:mybatis-plus-core:3.0.1") | ||||||
|  |     compileOnly("net.java.dev.jna:jna:5.17.0") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -97,10 +96,12 @@ tasks.withType<JavaExec>().configureEach { | |||||||
| 
 | 
 | ||||||
| tasks.withType<org.gradle.jvm.tasks.Jar> { | 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", | ||||||
| @ -131,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 { | ||||||
| @ -150,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") { | ||||||
| @ -158,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 { | ||||||
| @ -179,7 +209,6 @@ tasks.processResources { | |||||||
|                     DateTimeFormatter.ofPattern( |                     DateTimeFormatter.ofPattern( | ||||||
|                         "yyyy-MM-dd HH:mm:ss.SSSSSSS" |                         "yyyy-MM-dd HH:mm:ss.SSSSSSS" | ||||||
|                     ) |                     ) | ||||||
| 
 |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -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 17:24:10 | # 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.5 | VERSIONS=4.1.9 | ||||||
|  | signing.keyId=B22AA93B | ||||||
|  | signing.password= | ||||||
|  | signing.secretKeyRingFile=secret.gpg | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -16,7 +16,7 @@ | |||||||
| # ProjectName mingli-utils | # ProjectName mingli-utils | ||||||
| # ModuleName mingli-utils | # ModuleName mingli-utils | ||||||
| # CurrentFile gradle-wrapper.properties | # CurrentFile gradle-wrapper.properties | ||||||
| # LastUpdate 2025-09-15 12:01:36 | # LastUpdate 2025-09-15 22:32:50 | ||||||
| # UpdateUser MingLiPro | # UpdateUser MingLiPro | ||||||
| # | # | ||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							| @ -18,7 +18,7 @@ | |||||||
| # ProjectName mingli-utils | # ProjectName mingli-utils | ||||||
| # ModuleName mingli-utils | # ModuleName mingli-utils | ||||||
| # CurrentFile gradlew | # CurrentFile gradlew | ||||||
| # LastUpdate 2025-09-15 12:01:36 | # LastUpdate 2025-09-15 22:32:50 | ||||||
| # UpdateUser MingLiPro | # UpdateUser MingLiPro | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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-14 18:19:04 |  * 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
									
								
							
							
						
						
									
										23
									
								
								jdk8/gradle.properties
									
									
									
									
									
										Normal 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 | ||||||
| @ -1 +0,0 @@ | |||||||
| lombok.addLombokGeneratedAnnotation = false |  | ||||||
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @ -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" |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -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-13 02:37:04 |  * LastUpdate 2025-09-16 12:32:52 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,30 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile Main.java |  | ||||||
|  * LastUpdate 2025-09-15 11:18:12 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils; |  | ||||||
| 
 |  | ||||||
| public class Main { |  | ||||||
| 
 |  | ||||||
| 	public static void main(String[] args) { |  | ||||||
| 		MainKt.main(); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,249 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile GsonJsonApi.java |  | ||||||
|  * LastUpdate 2025-09-15 11:20:04 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.json; |  | ||||||
| 
 |  | ||||||
| import com.google.gson.*; |  | ||||||
| import com.mingliqiye.utils.json.converters.JsonConverter; |  | ||||||
| import com.mingliqiye.utils.json.converters.JsonStringConverter; |  | ||||||
| import org.jetbrains.annotations.NotNull; |  | ||||||
| 
 |  | ||||||
| public class GsonJsonApi implements JsonApi { |  | ||||||
| 
 |  | ||||||
| 	private Gson gsonUnicode; |  | ||||||
| 	private Gson gsonPretty; |  | ||||||
| 	private Gson gsonPrettyUnicode; |  | ||||||
| 	private Gson gson; |  | ||||||
| 
 |  | ||||||
| 	public GsonJsonApi() { |  | ||||||
| 		gson = new GsonBuilder() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 
 |  | ||||||
| 		gsonUnicode = new GsonBuilder() |  | ||||||
| 			.disableHtmlEscaping() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 
 |  | ||||||
| 		gsonPretty = new GsonBuilder() |  | ||||||
| 			.setPrettyPrinting() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 
 |  | ||||||
| 		gsonPrettyUnicode = new GsonBuilder() |  | ||||||
| 			.setPrettyPrinting() |  | ||||||
| 			.disableHtmlEscaping() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public GsonJsonApi(Gson gson) { |  | ||||||
| 		this.gson = gson |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 		this.gsonUnicode = gson |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.disableHtmlEscaping() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 		this.gsonPretty = gson |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.setPrettyPrinting() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 		this.gsonPrettyUnicode = gson |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.setPrettyPrinting() |  | ||||||
| 			.disableHtmlEscaping() |  | ||||||
| 			.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) |  | ||||||
| 			.create(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public <T> T parse(String json, Class<T> clazz) { |  | ||||||
| 		return gson.fromJson(json, clazz); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public <T> T parse(String json, JsonTypeReference<T> type) { |  | ||||||
| 		return gson.fromJson(json, type.getType()); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String format(Object object) { |  | ||||||
| 		return gson.toJson(object); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String formatUnicode(Object object) { |  | ||||||
| 		return gsonUnicode.toJson(object); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String formatPretty(Object object) { |  | ||||||
| 		return gsonPretty.toJson(object); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String formatPrettyUnicode(Object object) { |  | ||||||
| 		return gsonPrettyUnicode.toJson(object); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public boolean isValidJson(String json) { |  | ||||||
| 		try { |  | ||||||
| 			JsonElement element = JsonParser.parseString(json); |  | ||||||
| 			return true; |  | ||||||
| 		} catch (JsonSyntaxException e) { |  | ||||||
| 			return false; |  | ||||||
| 		} catch (Exception e) { |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String merge(String... jsons) { |  | ||||||
| 		JsonObject merged = new JsonObject(); |  | ||||||
| 		for (String json : jsons) { |  | ||||||
| 			if (json == null || json.isEmpty()) { |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 			try { |  | ||||||
| 				JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); |  | ||||||
| 				for (String key : obj.keySet()) { |  | ||||||
| 					merged.add(key, obj.get(key)); |  | ||||||
| 				} |  | ||||||
| 			} catch (Exception e) { |  | ||||||
| 				// 忽略无效的 JSON 字符串 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return gson.toJson(merged); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String getNodeValue(String json, String path) { |  | ||||||
| 		try { |  | ||||||
| 			JsonElement element = JsonParser.parseString(json); |  | ||||||
| 			String[] paths = path.split("\\."); |  | ||||||
| 			JsonElement current = element; |  | ||||||
| 
 |  | ||||||
| 			for (String p : paths) { |  | ||||||
| 				if (current.isJsonObject()) { |  | ||||||
| 					current = current.getAsJsonObject().get(p); |  | ||||||
| 				} else { |  | ||||||
| 					return null; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if (current == null) { |  | ||||||
| 					return null; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return current.isJsonPrimitive() |  | ||||||
| 				? current.getAsString() |  | ||||||
| 				: current.toString(); |  | ||||||
| 		} catch (Exception e) { |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String updateNodeValue(String json, String path, Object newValue) { |  | ||||||
| 		try { |  | ||||||
| 			JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); |  | ||||||
| 			String[] paths = path.split("\\."); |  | ||||||
| 			JsonObject current = obj; |  | ||||||
| 
 |  | ||||||
| 			// 导航到倒数第二层 |  | ||||||
| 			for (int i = 0; i < paths.length - 1; i++) { |  | ||||||
| 				String p = paths[i]; |  | ||||||
| 				if (!current.has(p) || !current.get(p).isJsonObject()) { |  | ||||||
| 					current.add(p, new JsonObject()); |  | ||||||
| 				} |  | ||||||
| 				current = current.getAsJsonObject(p); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// 设置最后一层的值 |  | ||||||
| 			String lastPath = paths[paths.length - 1]; |  | ||||||
| 			if (newValue == null) { |  | ||||||
| 				current.remove(lastPath); |  | ||||||
| 			} else { |  | ||||||
| 				JsonElement element = gson.toJsonTree(newValue); |  | ||||||
| 				current.add(lastPath, element); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return gson.toJson(obj); |  | ||||||
| 		} catch (Exception e) { |  | ||||||
| 			return json; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public <T, D> D convert(T source, Class<D> destinationClass) { |  | ||||||
| 		String json = gson.toJson(source); |  | ||||||
| 		return gson.fromJson(json, destinationClass); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public <T, D> D convert(T source, JsonTypeReference<D> destinationType) { |  | ||||||
| 		String json = gson.toJson(source); |  | ||||||
| 		return gson.fromJson(json, destinationType.getType()); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public void addJsonConverter(@NotNull JsonConverter<?, ?> c) { |  | ||||||
| 		gson = gson |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.registerTypeAdapter( |  | ||||||
| 				c.getTClass(), |  | ||||||
| 				c.getStringConverter().getGsonJsonStringConverterAdapter() |  | ||||||
| 			) |  | ||||||
| 			.create(); |  | ||||||
| 		gsonUnicode = gsonUnicode |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.registerTypeAdapter( |  | ||||||
| 				c.getTClass(), |  | ||||||
| 				c.getStringConverter().getGsonJsonStringConverterAdapter() |  | ||||||
| 			) |  | ||||||
| 			.create(); |  | ||||||
| 		gsonPretty = gsonPretty |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.registerTypeAdapter( |  | ||||||
| 				c.getTClass(), |  | ||||||
| 				c.getStringConverter().getGsonJsonStringConverterAdapter() |  | ||||||
| 			) |  | ||||||
| 			.create(); |  | ||||||
| 		gsonPrettyUnicode = gsonPrettyUnicode |  | ||||||
| 			.newBuilder() |  | ||||||
| 			.registerTypeAdapter( |  | ||||||
| 				c.getTClass(), |  | ||||||
| 				c.getStringConverter().getGsonJsonStringConverterAdapter() |  | ||||||
| 			) |  | ||||||
| 			.create(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public void addJsonStringConverter(@NotNull JsonStringConverter<?> c) { |  | ||||||
| 		addJsonConverter(c); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,355 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile JacksonJsonApi.java |  | ||||||
|  * LastUpdate 2025-09-15 11:16:53 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.json; |  | ||||||
| 
 |  | ||||||
| import com.fasterxml.jackson.core.JsonGenerator; |  | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; |  | ||||||
| import com.fasterxml.jackson.databind.JsonNode; |  | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; |  | ||||||
| import com.fasterxml.jackson.databind.ObjectReader; |  | ||||||
| import com.fasterxml.jackson.databind.node.ObjectNode; |  | ||||||
| import com.mingliqiye.utils.json.converters.JsonConverter; |  | ||||||
| import com.mingliqiye.utils.json.converters.JsonStringConverter; |  | ||||||
| import org.jetbrains.annotations.NotNull; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 基于Jackson的JSON处理实现类,提供JSON字符串解析、格式化、合并、节点操作等功能。 |  | ||||||
|  */ |  | ||||||
| public class JacksonJsonApi implements JsonApi { |  | ||||||
| 
 |  | ||||||
| 	private final ObjectMapper objectMapper; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 使用默认的ObjectMapper构造实例 |  | ||||||
| 	 */ |  | ||||||
| 	public JacksonJsonApi() { |  | ||||||
| 		this.objectMapper = new ObjectMapper(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 使用指定的ObjectMapper构造实例 |  | ||||||
| 	 * |  | ||||||
| 	 * @param objectMapper 自定义的ObjectMapper实例 |  | ||||||
| 	 */ |  | ||||||
| 	public JacksonJsonApi(ObjectMapper objectMapper) { |  | ||||||
| 		this.objectMapper = objectMapper.copy(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将JSON字符串解析为指定类型的对象 |  | ||||||
| 	 * |  | ||||||
| 	 * @param json  待解析的JSON字符串 |  | ||||||
| 	 * @param clazz 目标对象类型 |  | ||||||
| 	 * @param <T>   泛型参数,表示目标对象类型 |  | ||||||
| 	 * @return 解析后的对象 |  | ||||||
| 	 * @throws JsonException 当解析失败时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public <T> T parse(String json, Class<T> clazz) { |  | ||||||
| 		try { |  | ||||||
| 			return objectMapper.readValue(json, clazz); |  | ||||||
| 		} catch (IOException e) { |  | ||||||
| 			throw new JsonException("Failed to parse JSON string", e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将JSON字符串解析为复杂泛型结构的对象(如List、Map等) |  | ||||||
| 	 * |  | ||||||
| 	 * @param json JSON字符串 |  | ||||||
| 	 * @param type 泛型类型引用 |  | ||||||
| 	 * @param <T>  泛型参数,表示目标对象类型 |  | ||||||
| 	 * @return 解析后的对象 |  | ||||||
| 	 * @throws JsonException 当解析失败时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public <T> T parse(String json, JsonTypeReference<T> type) { |  | ||||||
| 		try { |  | ||||||
| 			ObjectReader reader = objectMapper.readerFor( |  | ||||||
| 				objectMapper.constructType(type.getType()) |  | ||||||
| 			); |  | ||||||
| 			return reader.readValue(json); |  | ||||||
| 		} catch (IOException e) { |  | ||||||
| 			throw new JsonException("Failed to parse JSON string", e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将对象格式化为JSON字符串 |  | ||||||
| 	 * |  | ||||||
| 	 * @param object 待格式化的对象 |  | ||||||
| 	 * @return 格式化后的JSON字符串 |  | ||||||
| 	 * @throws JsonException 当格式化失败时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public String format(Object object) { |  | ||||||
| 		try { |  | ||||||
| 			return objectMapper.writeValueAsString(object); |  | ||||||
| 		} catch (JsonProcessingException e) { |  | ||||||
| 			throw new JsonException( |  | ||||||
| 				"Failed to format object to JSON string", |  | ||||||
| 				e |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String formatUnicode(Object object) { |  | ||||||
| 		try { |  | ||||||
| 			return objectMapper |  | ||||||
| 				.writer() |  | ||||||
| 				.with(JsonGenerator.Feature.ESCAPE_NON_ASCII) |  | ||||||
| 				.writeValueAsString(object); |  | ||||||
| 		} catch (JsonProcessingException e) { |  | ||||||
| 			throw new JsonException(e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将对象格式化为美化(带缩进)的JSON字符串 |  | ||||||
| 	 * |  | ||||||
| 	 * @param object 待格式化的对象 |  | ||||||
| 	 * @return 美化后的JSON字符串 |  | ||||||
| 	 * @throws JsonException 当格式化失败时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public String formatPretty(Object object) { |  | ||||||
| 		try { |  | ||||||
| 			return objectMapper |  | ||||||
| 				.writerWithDefaultPrettyPrinter() |  | ||||||
| 				.writeValueAsString(object); |  | ||||||
| 		} catch (JsonProcessingException e) { |  | ||||||
| 			throw new JsonException( |  | ||||||
| 				"Failed to format object to pretty JSON string", |  | ||||||
| 				e |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String formatPrettyUnicode(Object object) { |  | ||||||
| 		try { |  | ||||||
| 			return objectMapper |  | ||||||
| 				.writerWithDefaultPrettyPrinter() |  | ||||||
| 				.with(JsonGenerator.Feature.ESCAPE_NON_ASCII) |  | ||||||
| 				.writeValueAsString(object); |  | ||||||
| 		} catch (JsonProcessingException e) { |  | ||||||
| 			throw new JsonException( |  | ||||||
| 				"Failed to format object to pretty JSON string", |  | ||||||
| 				e |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将JSON字符串解析为指定元素类型的List |  | ||||||
| 	 * |  | ||||||
| 	 * @param json        JSON字符串 |  | ||||||
| 	 * @param elementType List中元素的类型 |  | ||||||
| 	 * @param <T>         泛型参数,表示List中元素的类型 |  | ||||||
| 	 * @return 解析后的List对象 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public <T> List<T> parseList(String json, Class<T> elementType) { |  | ||||||
| 		return parse(json, JsonTypeUtils.listType(elementType)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将JSON字符串解析为指定键值类型的Map |  | ||||||
| 	 * |  | ||||||
| 	 * @param json      JSON字符串 |  | ||||||
| 	 * @param keyType   Map中键的类型 |  | ||||||
| 	 * @param valueType Map中值的类型 |  | ||||||
| 	 * @param <K>       泛型参数,表示Map中键的类型 |  | ||||||
| 	 * @param <V>       泛型参数,表示Map中值的类型 |  | ||||||
| 	 * @return 解析后的Map对象 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public <K, V> Map<K, V> parseMap( |  | ||||||
| 		String json, |  | ||||||
| 		Class<K> keyType, |  | ||||||
| 		Class<V> valueType |  | ||||||
| 	) { |  | ||||||
| 		return parse(json, JsonTypeUtils.MapType(keyType, valueType)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 判断给定字符串是否是有效的JSON格式 |  | ||||||
| 	 * |  | ||||||
| 	 * @param json 待验证的字符串 |  | ||||||
| 	 * @return 如果是有效JSON返回true,否则返回false |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public boolean isValidJson(String json) { |  | ||||||
| 		try { |  | ||||||
| 			objectMapper.readTree(json); |  | ||||||
| 			return true; |  | ||||||
| 		} catch (Exception e) { |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 合并多个JSON字符串为一个JSON对象 |  | ||||||
| 	 * |  | ||||||
| 	 * @param jsons 多个JSON字符串 |  | ||||||
| 	 * @return 合并后的JSON字符串 |  | ||||||
| 	 * @throws JsonException 当合并失败时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public String merge(String... jsons) { |  | ||||||
| 		ObjectNode result = objectMapper.createObjectNode(); |  | ||||||
| 		for (String json : jsons) { |  | ||||||
| 			try { |  | ||||||
| 				JsonNode node = objectMapper.readTree(json); |  | ||||||
| 				if (node.isObject()) { |  | ||||||
| 					result.setAll((ObjectNode) node); |  | ||||||
| 				} |  | ||||||
| 			} catch (IOException e) { |  | ||||||
| 				// 忽略无效的JSON字符串 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		try { |  | ||||||
| 			return objectMapper.writeValueAsString(result); |  | ||||||
| 		} catch (JsonProcessingException e) { |  | ||||||
| 			throw new JsonException("Failed to merge JSON strings", e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取JSON字符串中指定路径的节点值 |  | ||||||
| 	 * |  | ||||||
| 	 * @param json JSON字符串 |  | ||||||
| 	 * @param path 节点路径,使用"."分隔 |  | ||||||
| 	 * @return 节点值的文本表示,如果路径不存在则返回null |  | ||||||
| 	 * @throws JsonException 当获取节点值失败时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public String getNodeValue(String json, String path) { |  | ||||||
| 		try { |  | ||||||
| 			JsonNode node = objectMapper.readTree(json); |  | ||||||
| 			String[] paths = path.split("\\."); |  | ||||||
| 			for (String p : paths) { |  | ||||||
| 				node = node.get(p); |  | ||||||
| 				if (node == null) { |  | ||||||
| 					return null; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			return node.asText(); |  | ||||||
| 		} catch (IOException e) { |  | ||||||
| 			throw new JsonException("Failed to get node value", e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 更新JSON字符串中指定路径的节点值 |  | ||||||
| 	 * |  | ||||||
| 	 * @param json     JSON字符串 |  | ||||||
| 	 * @param path     节点路径,使用"."分隔 |  | ||||||
| 	 * @param newValue 新的节点值 |  | ||||||
| 	 * @return 更新后的JSON字符串 |  | ||||||
| 	 * @throws JsonException 当更新节点值失败时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public String updateNodeValue(String json, String path, Object newValue) { |  | ||||||
| 		try { |  | ||||||
| 			JsonNode node = objectMapper.readTree(json); |  | ||||||
| 			if (node instanceof ObjectNode) { |  | ||||||
| 				ObjectNode objectNode = (ObjectNode) node; |  | ||||||
| 				String[] paths = path.split("\\."); |  | ||||||
| 				JsonNode current = objectNode; |  | ||||||
| 
 |  | ||||||
| 				// 导航到目标节点的父节点 |  | ||||||
| 				for (int i = 0; i < paths.length - 1; i++) { |  | ||||||
| 					current = current.get(paths[i]); |  | ||||||
| 					if (current == null || !(current instanceof ObjectNode)) { |  | ||||||
| 						return json; // 路径不存在或无效 |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				// 更新值 |  | ||||||
| 				if (current instanceof ObjectNode) { |  | ||||||
| 					ObjectNode parent = (ObjectNode) current; |  | ||||||
| 					if (newValue == null) { |  | ||||||
| 						parent.remove(paths[paths.length - 1]); |  | ||||||
| 					} else { |  | ||||||
| 						parent.set( |  | ||||||
| 							paths[paths.length - 1], |  | ||||||
| 							objectMapper.valueToTree(newValue) |  | ||||||
| 						); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				return objectMapper.writeValueAsString(objectNode); |  | ||||||
| 			} |  | ||||||
| 			return json; |  | ||||||
| 		} catch (IOException e) { |  | ||||||
| 			throw new JsonException("Failed to update node value", e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 在不同对象类型之间进行转换 |  | ||||||
| 	 * |  | ||||||
| 	 * @param source           源对象 |  | ||||||
| 	 * @param destinationClass 目标对象类型 |  | ||||||
| 	 * @param <T>              源对象类型 |  | ||||||
| 	 * @param <D>              目标对象类型 |  | ||||||
| 	 * @return 转换后的对象 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public <T, D> D convert(T source, Class<D> destinationClass) { |  | ||||||
| 		return objectMapper.convertValue(source, destinationClass); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 在不同泛型对象类型之间进行转换 |  | ||||||
| 	 * |  | ||||||
| 	 * @param source          源对象 |  | ||||||
| 	 * @param destinationType 目标对象的泛型类型引用 |  | ||||||
| 	 * @param <T>             源对象类型 |  | ||||||
| 	 * @param <D>             目标对象类型 |  | ||||||
| 	 * @return 转换后的对象 |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public <T, D> D convert(T source, JsonTypeReference<D> destinationType) { |  | ||||||
| 		return objectMapper.convertValue( |  | ||||||
| 			source, |  | ||||||
| 			objectMapper.constructType(destinationType.getType()) |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public void addJsonConverter(@NotNull JsonConverter<?, ?> c) { |  | ||||||
| 		objectMapper.registerModule(c.getStringConverter().getJacksonJsonStringConverterAdapter().getJacksonModule()); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public void addJsonStringConverter(@NotNull JsonStringConverter<?> c) { |  | ||||||
| 		addJsonConverter(c); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,38 +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 JsonException.java |  | ||||||
|  * LastUpdate 2025-09-09 09:25:08 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.json; |  | ||||||
| 
 |  | ||||||
| public class JsonException extends RuntimeException { |  | ||||||
| 
 |  | ||||||
| 	public JsonException(String message) { |  | ||||||
| 		super(message); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public JsonException(String message, Throwable cause) { |  | ||||||
| 		super(message, cause); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public JsonException(Throwable cause) { |  | ||||||
| 		this(cause.getMessage(), cause); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,175 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile JsonTypeReference.java |  | ||||||
|  * LastUpdate 2025-09-09 09:20:05 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.json; |  | ||||||
| 
 |  | ||||||
| import java.lang.reflect.ParameterizedType; |  | ||||||
| import java.lang.reflect.Type; |  | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.Objects; |  | ||||||
| import lombok.Getter; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 通用的 JSON 类型引用类,用于在运行时保留泛型类型信息 |  | ||||||
|  * 适用于所有 JSON 库(Jackson、Gson、Fastjson 等) |  | ||||||
|  * |  | ||||||
|  * @param <T> 引用的泛型类型 |  | ||||||
|  */ |  | ||||||
| @Getter |  | ||||||
| public abstract class JsonTypeReference<T> |  | ||||||
| 	implements Comparable<JsonTypeReference<T>> { |  | ||||||
| 
 |  | ||||||
| 	protected final Type type; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 构造函数,通过反射获取泛型类型信息 |  | ||||||
| 	 * 仅供内部匿名子类使用 |  | ||||||
| 	 */ |  | ||||||
| 	protected JsonTypeReference() { |  | ||||||
| 		Type superClass = getClass().getGenericSuperclass(); |  | ||||||
| 
 |  | ||||||
| 		// 检查是否为匿名子类,防止直接实例化导致无法获取泛型信息 |  | ||||||
| 		if (superClass instanceof Class) { |  | ||||||
| 			throw new IllegalArgumentException( |  | ||||||
| 				"必须使用匿名子类方式创建 JsonTypeReference," + |  | ||||||
| 				"例如: new JsonTypeReference<List<String>>() {}" |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		this.type = |  | ||||||
| 			((ParameterizedType) superClass).getActualTypeArguments()[0]; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 构造函数,直接指定类型 |  | ||||||
| 	 * @param type 具体的类型信息 |  | ||||||
| 	 */ |  | ||||||
| 	protected JsonTypeReference(Type type) { |  | ||||||
| 		this.type = Objects.requireNonNull(type, "Type cannot be null"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 创建类型引用实例 |  | ||||||
| 	 * @param <T> 目标类型 |  | ||||||
| 	 * @return 类型引用实例 |  | ||||||
| 	 */ |  | ||||||
| 	public static <T> JsonTypeReference<T> of() { |  | ||||||
| 		return new JsonTypeReference<T>() {}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 根据 Class 创建类型引用 |  | ||||||
| 	 * @param clazz 目标类 |  | ||||||
| 	 * @param <T> 目标类型 |  | ||||||
| 	 * @return 类型引用实例 |  | ||||||
| 	 */ |  | ||||||
| 	public static <T> JsonTypeReference<T> of(Class<T> clazz) { |  | ||||||
| 		return new JsonTypeReference<T>(clazz) {}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 根据 Type 创建类型引用 |  | ||||||
| 	 * @param type 目标类型 |  | ||||||
| 	 * @param <T> 目标类型 |  | ||||||
| 	 * @return 类型引用实例 |  | ||||||
| 	 */ |  | ||||||
| 	public static <T> JsonTypeReference<T> of(Type type) { |  | ||||||
| 		return new JsonTypeReference<T>(type) {}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取原始类型(去掉泛型参数的类型) |  | ||||||
| 	 * @return 原始类型 Class |  | ||||||
| 	 */ |  | ||||||
| 	@SuppressWarnings("unchecked") |  | ||||||
| 	public Class<T> getRawType() { |  | ||||||
| 		Type rawType = type; |  | ||||||
| 
 |  | ||||||
| 		// 如果是参数化类型,则提取原始类型部分 |  | ||||||
| 		if (type instanceof ParameterizedType) { |  | ||||||
| 			rawType = ((ParameterizedType) type).getRawType(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (rawType instanceof Class) { |  | ||||||
| 			return (Class<T>) rawType; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		throw new IllegalStateException("无法获取原始类型: " + type); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public boolean equals(Object o) { |  | ||||||
| 		if (this == o) return true; |  | ||||||
| 		if (o == null || getClass() != o.getClass()) return false; |  | ||||||
| 		JsonTypeReference<?> that = (JsonTypeReference<?>) o; |  | ||||||
| 
 |  | ||||||
| 		// 对于 ParameterizedType,需要更完整的比较 |  | ||||||
| 		if ( |  | ||||||
| 			this.type instanceof ParameterizedType && |  | ||||||
| 			that.type instanceof ParameterizedType |  | ||||||
| 		) { |  | ||||||
| 			ParameterizedType thisParamType = (ParameterizedType) this.type; |  | ||||||
| 			ParameterizedType thatParamType = (ParameterizedType) that.type; |  | ||||||
| 
 |  | ||||||
| 			return ( |  | ||||||
| 				Objects.equals( |  | ||||||
| 					thisParamType.getRawType(), |  | ||||||
| 					thatParamType.getRawType() |  | ||||||
| 				) && |  | ||||||
| 				Arrays.equals( |  | ||||||
| 					thisParamType.getActualTypeArguments(), |  | ||||||
| 					thatParamType.getActualTypeArguments() |  | ||||||
| 				) && |  | ||||||
| 				Objects.equals( |  | ||||||
| 					thisParamType.getOwnerType(), |  | ||||||
| 					thatParamType.getOwnerType() |  | ||||||
| 				) |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return Objects.equals(type, that.type); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public int hashCode() { |  | ||||||
| 		// 针对 ParameterizedType 进行完整哈希计算 |  | ||||||
| 		if (type instanceof ParameterizedType) { |  | ||||||
| 			ParameterizedType paramType = (ParameterizedType) type; |  | ||||||
| 			return Objects.hash( |  | ||||||
| 				paramType.getRawType(), |  | ||||||
| 				Arrays.hashCode(paramType.getActualTypeArguments()), |  | ||||||
| 				paramType.getOwnerType() |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 		return Objects.hash(type); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public String toString() { |  | ||||||
| 		return "JsonTypeReference{" + type + '}'; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override |  | ||||||
| 	public int compareTo(JsonTypeReference<T> o) { |  | ||||||
| 		return this.type.toString().compareTo(o.type.toString()); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,253 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile JsonTypeUtils.java |  | ||||||
|  * LastUpdate 2025-09-09 09:18:08 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.json; |  | ||||||
| 
 |  | ||||||
| import java.lang.reflect.ParameterizedType; |  | ||||||
| import java.lang.reflect.Type; |  | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; |  | ||||||
| import java.util.Objects; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * JSON 类型工具类,提供类型相关的工具方法 |  | ||||||
|  */ |  | ||||||
| public class JsonTypeUtils { |  | ||||||
| 
 |  | ||||||
| 	private JsonTypeUtils() { |  | ||||||
| 		// 工具类,防止实例化 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 检查给定的类型是否是指定类或其子类/实现类。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param type          要检查的类型 |  | ||||||
| 	 * @param expectedClass 期望匹配的类 |  | ||||||
| 	 * @return 如果类型匹配则返回 true,否则返回 false |  | ||||||
| 	 */ |  | ||||||
| 	public static boolean isTypeOf(Type type, Class<?> expectedClass) { |  | ||||||
| 		if (type instanceof Class) { |  | ||||||
| 			return expectedClass.isAssignableFrom((Class<?>) type); |  | ||||||
| 		} else if (type instanceof ParameterizedType) { |  | ||||||
| 			return isTypeOf( |  | ||||||
| 				((ParameterizedType) type).getRawType(), |  | ||||||
| 				expectedClass |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取泛型类型的参数类型。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param type  泛型类型 |  | ||||||
| 	 * @param index 参数索引(从0开始) |  | ||||||
| 	 * @return 指定位置的泛型参数类型 |  | ||||||
| 	 * @throws IllegalArgumentException 当无法获取指定索引的泛型参数时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	public static Type getGenericParameter(Type type, int index) { |  | ||||||
| 		if (type instanceof ParameterizedType) { |  | ||||||
| 			Type[] typeArgs = |  | ||||||
| 				((ParameterizedType) type).getActualTypeArguments(); |  | ||||||
| 			if (index >= 0 && index < typeArgs.length) { |  | ||||||
| 				return typeArgs[index]; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		throw new IllegalArgumentException( |  | ||||||
| 			"无法获取泛型参数: " + type + " at index " + index |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取类型名称,支持普通类和泛型类型。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param type 类型对象 |  | ||||||
| 	 * @return 类型名称字符串 |  | ||||||
| 	 */ |  | ||||||
| 	public static String getTypeName(Type type) { |  | ||||||
| 		if (type instanceof Class) { |  | ||||||
| 			return ((Class<?>) type).getSimpleName(); |  | ||||||
| 		} else if (type instanceof ParameterizedType) { |  | ||||||
| 			ParameterizedType pType = (ParameterizedType) type; |  | ||||||
| 			Class<?> rawType = (Class<?>) pType.getRawType(); |  | ||||||
| 			Type[] typeArgs = pType.getActualTypeArguments(); |  | ||||||
| 
 |  | ||||||
| 			StringBuilder sb = new StringBuilder(rawType.getSimpleName()); |  | ||||||
| 			sb.append("<"); |  | ||||||
| 			for (int i = 0; i < typeArgs.length; i++) { |  | ||||||
| 				if (i > 0) sb.append(", "); |  | ||||||
| 				sb.append(getTypeName(typeArgs[i])); |  | ||||||
| 			} |  | ||||||
| 			sb.append(">"); |  | ||||||
| 			return sb.toString(); |  | ||||||
| 		} |  | ||||||
| 		return type.getTypeName(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 创建一个表示数组类型的引用对象。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param componentType 数组元素的类型 |  | ||||||
| 	 * @param <T>           元素类型 |  | ||||||
| 	 * @return 表示数组类型的 JsonTypeReference 对象 |  | ||||||
| 	 */ |  | ||||||
| 	public static <T> JsonTypeReference<T[]> arrayType(Class<T> componentType) { |  | ||||||
| 		return new JsonTypeReference<T[]>() { |  | ||||||
| 			private final Type arrayType = java.lang.reflect.Array.newInstance( |  | ||||||
| 				componentType, |  | ||||||
| 				0 |  | ||||||
| 			).getClass(); |  | ||||||
| 
 |  | ||||||
| 			@Override |  | ||||||
| 			public Type getType() { |  | ||||||
| 				return new ParameterizedType() { |  | ||||||
| 					private final Type[] actualTypeArguments = new Type[] { |  | ||||||
| 						componentType, |  | ||||||
| 					}; |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public Type[] getActualTypeArguments() { |  | ||||||
| 						return actualTypeArguments; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public Type getRawType() { |  | ||||||
| 						return arrayType; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public Type getOwnerType() { |  | ||||||
| 						return null; |  | ||||||
| 					} |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 创建一个表示 List 类型的引用对象。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param componentType List 中元素的类型 |  | ||||||
| 	 * @param <T>           元素类型 |  | ||||||
| 	 * @return 表示 List 类型的 JsonTypeReference 对象 |  | ||||||
| 	 * @throws IllegalArgumentException 如果 componentType 为 null,则抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	public static <T> JsonTypeReference<List<T>> listType( |  | ||||||
| 		Class<T> componentType |  | ||||||
| 	) { |  | ||||||
| 		if (componentType == null) { |  | ||||||
| 			throw new IllegalArgumentException("componentType cannot be null"); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return new JsonTypeReference<List<T>>() { |  | ||||||
| 			@Override |  | ||||||
| 			public Type getType() { |  | ||||||
| 				return new ParameterizedType() { |  | ||||||
| 					@Override |  | ||||||
| 					public Type[] getActualTypeArguments() { |  | ||||||
| 						return new Type[] { componentType }; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public Type getRawType() { |  | ||||||
| 						return List.class; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public Type getOwnerType() { |  | ||||||
| 						return null; |  | ||||||
| 					} |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 创建一个表示 Map 类型的引用对象。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param keyType   Map 键的类型 |  | ||||||
| 	 * @param valueType Map 值的类型 |  | ||||||
| 	 * @param <K>       键类型 |  | ||||||
| 	 * @param <V>       值类型 |  | ||||||
| 	 * @return 表示 Map 类型的 JsonTypeReference 对象 |  | ||||||
| 	 * @throws IllegalArgumentException 如果 keyType 或 valueType 为 null,则抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	public static <K, V> JsonTypeReference<Map<K, V>> MapType( |  | ||||||
| 		Class<K> keyType, |  | ||||||
| 		Class<V> valueType |  | ||||||
| 	) { |  | ||||||
| 		if (keyType == null) { |  | ||||||
| 			throw new IllegalArgumentException("keyType cannot be null"); |  | ||||||
| 		} |  | ||||||
| 		if (valueType == null) { |  | ||||||
| 			throw new IllegalArgumentException("valueType cannot be null"); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return new JsonTypeReference<Map<K, V>>() { |  | ||||||
| 			@Override |  | ||||||
| 			public Type getType() { |  | ||||||
| 				return new ParameterizedType() { |  | ||||||
| 					@Override |  | ||||||
| 					public Type[] getActualTypeArguments() { |  | ||||||
| 						return new Type[] { keyType, valueType }; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public Type getRawType() { |  | ||||||
| 						return Map.class; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public Type getOwnerType() { |  | ||||||
| 						return null; |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public boolean equals(Object obj) { |  | ||||||
| 						if (this == obj) return true; |  | ||||||
| 						if (!(obj instanceof ParameterizedType)) return false; |  | ||||||
| 
 |  | ||||||
| 						ParameterizedType that = (ParameterizedType) obj; |  | ||||||
| 						return ( |  | ||||||
| 							Objects.equals(getRawType(), that.getRawType()) && |  | ||||||
| 							Arrays.equals( |  | ||||||
| 								getActualTypeArguments(), |  | ||||||
| 								that.getActualTypeArguments() |  | ||||||
| 							) && |  | ||||||
| 							Objects.equals(getOwnerType(), that.getOwnerType()) |  | ||||||
| 						); |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					@Override |  | ||||||
| 					public int hashCode() { |  | ||||||
| 						return ( |  | ||||||
| 							Arrays.hashCode(getActualTypeArguments()) ^ |  | ||||||
| 							Objects.hashCode(getRawType()) ^ |  | ||||||
| 							Objects.hashCode(getOwnerType()) |  | ||||||
| 						); |  | ||||||
| 					} |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,32 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile Description.java |  | ||||||
|  * LastUpdate 2025-09-09 08:37:33 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.minecraft.slp; |  | ||||||
| 
 |  | ||||||
| import lombok.Data; |  | ||||||
| 
 |  | ||||||
| @Data |  | ||||||
| public class Description { |  | ||||||
| 
 |  | ||||||
| 	private String text; |  | ||||||
| 	private Extra[] extra; |  | ||||||
| } |  | ||||||
| @ -1,34 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile Extra.java |  | ||||||
|  * LastUpdate 2025-09-09 08:37:34 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.minecraft.slp; |  | ||||||
| 
 |  | ||||||
| import lombok.Data; |  | ||||||
| 
 |  | ||||||
| @Data |  | ||||||
| public class Extra { |  | ||||||
| 
 |  | ||||||
| 	private String text; |  | ||||||
| 	private String color; |  | ||||||
| 	private Boolean bold; |  | ||||||
| 	private Boolean italic; |  | ||||||
| } |  | ||||||
| @ -1,37 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile MinecraftServerStatus.java |  | ||||||
|  * LastUpdate 2025-09-09 08:37:33 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.minecraft.slp; |  | ||||||
| 
 |  | ||||||
| import lombok.Data; |  | ||||||
| 
 |  | ||||||
| @Data |  | ||||||
| public class MinecraftServerStatus { |  | ||||||
| 
 |  | ||||||
| 	private Description description; |  | ||||||
| 	private Players players; |  | ||||||
| 	private Version version; |  | ||||||
| 	private String favicon; |  | ||||||
| 	private boolean enforcesSecureChat; |  | ||||||
| 	private boolean previewsChat; |  | ||||||
| 	private String jsonData; |  | ||||||
| } |  | ||||||
| @ -1,219 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile SLP.java |  | ||||||
|  * LastUpdate 2025-09-09 08:37:33 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.minecraft.slp; |  | ||||||
| 
 |  | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; |  | ||||||
| import com.mingliqiye.utils.network.NetworkEndpoint; |  | ||||||
| import java.io.ByteArrayOutputStream; |  | ||||||
| import java.io.DataInputStream; |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.OutputStream; |  | ||||||
| import java.net.Socket; |  | ||||||
| import java.nio.ByteBuffer; |  | ||||||
| import java.nio.ByteOrder; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Minecraft 服务器列表协议(Server List Ping, SLP)工具类。 |  | ||||||
|  * 提供了与 Minecraft 服务器通信以获取其状态信息的功能。 |  | ||||||
|  */ |  | ||||||
| public class SLP { |  | ||||||
| 
 |  | ||||||
| 	private static final ObjectMapper objectMapper = new ObjectMapper(); |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将 int32 值截断为无符号 short(2 字节)并按大端序写入字节数组。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param value 需要转换的整数(int32) |  | ||||||
| 	 * @return 包含两个字节的数组,表示无符号 short |  | ||||||
| 	 */ |  | ||||||
| 	public static byte[] toUnsignedShort(int value) { |  | ||||||
| 		byte[] array = new byte[2]; |  | ||||||
| 		ByteBuffer.wrap(array, 0, 2) |  | ||||||
| 			.order(ByteOrder.BIG_ENDIAN) |  | ||||||
| 			.putShort((short) (value & 0xFFFF)); |  | ||||||
| 		return array; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 构造 Minecraft 握手包数据。 |  | ||||||
| 	 * 握手包用于初始化客户端与服务器之间的连接。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param serverIP   服务器 IP 地址或域名 |  | ||||||
| 	 * @param serverPort 服务器端口号 |  | ||||||
| 	 * @param type       连接类型(通常为 1 表示获取状态) |  | ||||||
| 	 * @return 握手包的完整字节数组 |  | ||||||
| 	 * @throws IOException 如果构造过程中发生 IO 错误 |  | ||||||
| 	 */ |  | ||||||
| 	public static byte[] getHandshakePack( |  | ||||||
| 		String serverIP, |  | ||||||
| 		int serverPort, |  | ||||||
| 		int type |  | ||||||
| 	) throws IOException { |  | ||||||
| 		ByteArrayOutputStream pack = new ByteArrayOutputStream(); |  | ||||||
| 		ByteArrayOutputStream byteArrayOutputStream = |  | ||||||
| 			new ByteArrayOutputStream(); |  | ||||||
| 		pack.write(0x00); // 握手包标识符 |  | ||||||
| 		pack.write(toVarInt(1156)); // 协议版本号(示例值) |  | ||||||
| 		byte[] sip = serverIP.getBytes(); |  | ||||||
| 		pack.write(toVarInt(sip.length)); // 服务器地址长度 |  | ||||||
| 		pack.write(sip); // 服务器地址 |  | ||||||
| 		pack.write(toUnsignedShort(serverPort)); // 服务器端口 |  | ||||||
| 		pack.write(toVarInt(type)); // 下一阶段类型(1 表示状态请求) |  | ||||||
| 		byteArrayOutputStream.write(toVarInt(pack.size())); // 包长度前缀 |  | ||||||
| 		byteArrayOutputStream.write(pack.toByteArray()); |  | ||||||
| 
 |  | ||||||
| 		return byteArrayOutputStream.toByteArray(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取状态请求包的固定字节表示。 |  | ||||||
| 	 * 此包用于向服务器请求当前状态信息。 |  | ||||||
| 	 * |  | ||||||
| 	 * @return 状态请求包的字节数组 |  | ||||||
| 	 */ |  | ||||||
| 	public static byte[] getStatusPack() { |  | ||||||
| 		return new byte[] { 0x01, 0x00 }; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 从输入流中读取服务器返回的状态 JSON 数据,并解析为 MinecraftServerStatus 实体对象。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param inputStream 输入流,包含服务器响应的数据 |  | ||||||
| 	 * @return 解析后的 MinecraftServerStatus 对象 |  | ||||||
| 	 * @throws IOException 如果读取过程中发生 IO 错误 |  | ||||||
| 	 */ |  | ||||||
| 	public static MinecraftServerStatus getStatusJsonEntity( |  | ||||||
| 		DataInputStream inputStream |  | ||||||
| 	) throws IOException { |  | ||||||
| 		readVarInt(inputStream); // 忽略第一个 VarInt(包长度) |  | ||||||
| 		inputStream.readByte(); // 忽略包标识符 |  | ||||||
| 		int lengthjson = readVarInt(inputStream); // 读取 JSON 数据长度 |  | ||||||
| 		byte[] data = new byte[lengthjson]; |  | ||||||
| 		inputStream.readFully(data); // 读取完整的 JSON 数据 |  | ||||||
| 		MinecraftServerStatus serverStatus = objectMapper.readValue( |  | ||||||
| 			data, |  | ||||||
| 			MinecraftServerStatus.class |  | ||||||
| 		); |  | ||||||
| 		serverStatus.setJsonData(new String(data)); // 设置原始 JSON 字符串 |  | ||||||
| 		return serverStatus; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 从输入流中读取一个 VarInt 类型的整数(最多 5 个字节)。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param in 输入流 |  | ||||||
| 	 * @return 解码后的整数值 |  | ||||||
| 	 * @throws IOException 如果读取过程中发生 IO 错误 |  | ||||||
| 	 */ |  | ||||||
| 	public static int readVarInt(DataInputStream in) throws IOException { |  | ||||||
| 		int value = 0; |  | ||||||
| 		int length = 0; |  | ||||||
| 		byte currentByte; |  | ||||||
| 		do { |  | ||||||
| 			currentByte = in.readByte(); |  | ||||||
| 			value |= (currentByte & 0x7F) << (length * 7); |  | ||||||
| 			length += 1; |  | ||||||
| 			if (length > 5) { |  | ||||||
| 				throw new RuntimeException("VarInt too long"); |  | ||||||
| 			} |  | ||||||
| 		} while ((currentByte & 0x80) != 0); |  | ||||||
| 		return value; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将一个 int32 整数编码为 VarInt 格式的字节数组(1 到 5 个字节)。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param value 需要编码的整数 |  | ||||||
| 	 * @return 编码后的 VarInt 字节数组 |  | ||||||
| 	 */ |  | ||||||
| 	public static byte[] toVarInt(int value) { |  | ||||||
| 		ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |  | ||||||
| 		while (true) { |  | ||||||
| 			if ((value & 0xFFFFFF80) == 0) { |  | ||||||
| 				buffer.write(value); // 最后一个字节 |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 			buffer.write((value & 0x7F) | 0x80); // 写入带继续位的字节 |  | ||||||
| 			value >>>= 7; // 右移 7 位继续处理 |  | ||||||
| 		} |  | ||||||
| 		return buffer.toByteArray(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 创建一个新的 Socket 连接到指定的网络端点,并设置超时时间。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param networkEndpoint 目标网络端点(包括主机和端口) |  | ||||||
| 	 * @return 已连接的 Socket 实例 |  | ||||||
| 	 * @throws IOException 如果连接失败或发生 IO 错误 |  | ||||||
| 	 */ |  | ||||||
| 	public static Socket getNewConnect(NetworkEndpoint networkEndpoint) |  | ||||||
| 		throws IOException { |  | ||||||
| 		Socket socket = new Socket(); |  | ||||||
| 		socket.setSoTimeout(5000); // 设置读取超时时间为 5 秒 |  | ||||||
| 		socket.connect(networkEndpoint.toInetSocketAddress()); // 执行连接操作 |  | ||||||
| 		return socket; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 使用 "host:port" 格式的字符串连接到 Minecraft 服务器并获取其状态信息。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param s 域名或 IP 地址加端口号组成的字符串,例如 "127.0.0.1:25565" |  | ||||||
| 	 * @return 服务器状态实体对象 |  | ||||||
| 	 * @throws IOException 如果连接失败或发生 IO 错误 |  | ||||||
| 	 */ |  | ||||||
| 	public static MinecraftServerStatus getServerStatus(String s) |  | ||||||
| 		throws IOException { |  | ||||||
| 		return getServerStatus(NetworkEndpoint.of(s)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 使用指定的主机名和端口号连接到 Minecraft 服务器并获取其状态信息。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param s 主机名或 IP 地址 |  | ||||||
| 	 * @param i 端口号 |  | ||||||
| 	 * @return 服务器状态实体对象 |  | ||||||
| 	 * @throws IOException 如果连接失败或发生 IO 错误 |  | ||||||
| 	 */ |  | ||||||
| 	public static MinecraftServerStatus getServerStatus(String s, Integer i) |  | ||||||
| 		throws IOException { |  | ||||||
| 		return getServerStatus(NetworkEndpoint.of(s, i)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 使用 NetworkEndpoint 实例连接到 Minecraft 服务器并获取其状态信息。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param e 网络端点实例,包含主机和端口信息 |  | ||||||
| 	 * @return 服务器状态实体对象 |  | ||||||
| 	 * @throws IOException 如果连接失败或发生 IO 错误 |  | ||||||
| 	 * @see NetworkEndpoint |  | ||||||
| 	 */ |  | ||||||
| 	public static MinecraftServerStatus getServerStatus(NetworkEndpoint e) |  | ||||||
| 		throws IOException { |  | ||||||
| 		Socket socket = getNewConnect(e); // 建立 TCP 连接 |  | ||||||
| 		OutputStream out = socket.getOutputStream(); // 获取输出流发送数据 |  | ||||||
| 		DataInputStream in = new DataInputStream(socket.getInputStream()); // 获取输入流接收数据 |  | ||||||
| 		out.write(getHandshakePack(e.getHost(), e.getPort(), 1)); // 发送握手包 |  | ||||||
| 		out.write(getStatusPack()); // 发送状态请求包 |  | ||||||
| 		return getStatusJsonEntity(in); // 读取并解析服务器响应 |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,32 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile Version.java |  | ||||||
|  * LastUpdate 2025-09-09 08:37:33 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.minecraft.slp; |  | ||||||
| 
 |  | ||||||
| import lombok.Data; |  | ||||||
| 
 |  | ||||||
| @Data |  | ||||||
| public class Version { |  | ||||||
| 
 |  | ||||||
| 	private String name; |  | ||||||
| 	private int protocol; |  | ||||||
| } |  | ||||||
| @ -1,218 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile NetworkAddress.java |  | ||||||
|  * LastUpdate 2025-09-14 22:12:16 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.network; |  | ||||||
| 
 |  | ||||||
| import com.mingliqiye.utils.string.StringUtils; |  | ||||||
| import lombok.Getter; |  | ||||||
| import org.jetbrains.annotations.NotNull; |  | ||||||
| 
 |  | ||||||
| import java.io.Serializable; |  | ||||||
| import java.net.InetAddress; |  | ||||||
| import java.net.UnknownHostException; |  | ||||||
| import java.util.regex.Pattern; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 网络地址类,用于表示一个网络地址(IP或域名),并提供相关操作。 |  | ||||||
|  * 支持IPv4和IPv6地址的解析与验证。 |  | ||||||
|  * |  | ||||||
|  * @author MingLiPro |  | ||||||
|  */ |  | ||||||
| public class NetworkAddress implements Serializable { |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * IPv6标识 |  | ||||||
| 	 */ |  | ||||||
| 	public static int IPV6 = 6; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * IPv4标识 |  | ||||||
| 	 */ |  | ||||||
| 	public static int IPV4 = 4; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * IPv4地址正则表达式 |  | ||||||
| 	 */ |  | ||||||
| 	static String IPV4REG = |  | ||||||
| 		"^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" + |  | ||||||
| 		"(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$"; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 编译后的IPv4地址匹配模式 |  | ||||||
| 	 */ |  | ||||||
| 	private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4REG); |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * IPv6地址正则表达式 |  | ||||||
| 	 */ |  | ||||||
| 	static String IPV6REG = |  | ||||||
| 		"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" + |  | ||||||
| 		"^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" + |  | ||||||
| 		"|" + |  | ||||||
| 		"^(::)$|" + |  | ||||||
| 		"^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" + |  | ||||||
| 		"^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" + |  | ||||||
| 		"^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" + |  | ||||||
| 		"^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$"; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 编译后的IPv6地址匹配模式 |  | ||||||
| 	 */ |  | ||||||
| 	private static final Pattern IPV6_PATTERN = Pattern.compile(IPV6REG); |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * IP地址类型:4 表示 IPv4,6 表示 IPv6 |  | ||||||
| 	 */ |  | ||||||
| 	@Getter |  | ||||||
| 	private int IPv; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * IP地址字符串 |  | ||||||
| 	 */ |  | ||||||
| 	@Getter |  | ||||||
| 	private String ip; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 域名(如果输入的是域名) |  | ||||||
| 	 */ |  | ||||||
| 	private String domain; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 标识是否是域名解析来的IP |  | ||||||
| 	 */ |  | ||||||
| 	private boolean isdom; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 构造方法,根据传入的字符串判断是IP地址还是域名,并进行相应处理。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param domip 可能是IP地址或域名的字符串 |  | ||||||
| 	 */ |  | ||||||
| 	NetworkAddress(String domip) { |  | ||||||
| 		try { |  | ||||||
| 			// 尝试将输入识别为IP地址 |  | ||||||
| 			IPv = testIp(domip); |  | ||||||
| 			ip = domip; |  | ||||||
| 		} catch (NetworkException e) { |  | ||||||
| 			try { |  | ||||||
| 				// 如果不是有效IP,则尝试作为域名解析 |  | ||||||
| 				String ips = getHostIp(domip); |  | ||||||
| 				IPv = testIp(ips); |  | ||||||
| 				ip = ips; |  | ||||||
| 				isdom = true; |  | ||||||
| 				domain = domip; |  | ||||||
| 			} catch (UnknownHostException ex) { |  | ||||||
| 				throw new NetworkException(ex); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 静态工厂方法,创建 NetworkAddress 实例。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param domip 可能是IP地址或域名的字符串 |  | ||||||
| 	 * @return 新建的 NetworkAddress 实例 |  | ||||||
| 	 */ |  | ||||||
| 	public static NetworkAddress of(String domip) { |  | ||||||
| 		return new NetworkAddress(domip); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 静态工厂方法,通过 InetAddress 创建 NetworkAddress 实例。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param inetAddress InetAddress 对象 |  | ||||||
| 	 * @return 新建的 NetworkAddress 实例 |  | ||||||
| 	 */ |  | ||||||
| 	public static NetworkAddress of(InetAddress inetAddress) { |  | ||||||
| 		return new NetworkAddress(inetAddress.getHostAddress()); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 从DNS服务器解析域名获取对应的IP地址。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param domain 域名 |  | ||||||
| 	 * @return 解析出的第一个IP地址 |  | ||||||
| 	 * @throws UnknownHostException 如果域名无法解析 |  | ||||||
| 	 */ |  | ||||||
| 	public static String getHostIp(@NotNull String domain) |  | ||||||
| 		throws UnknownHostException { |  | ||||||
| 		InetAddress[] addresses = InetAddress.getAllByName(domain.trim()); |  | ||||||
| 		return addresses[0].getHostAddress(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 检测给定字符串是否为有效的IPv4或IPv6地址。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param ip 要检测的IP地址字符串 |  | ||||||
| 	 * @return 4 表示IPv4,6 表示IPv6 |  | ||||||
| 	 * @throws NetworkException 如果IP格式无效 |  | ||||||
| 	 */ |  | ||||||
| 	public static int testIp(String ip) { |  | ||||||
| 		if (ip == null) { |  | ||||||
| 			throw new NetworkException("IP地址不能为null"); |  | ||||||
| 		} |  | ||||||
| 		String trimmedIp = ip.trim(); |  | ||||||
| 
 |  | ||||||
| 		// 判断是否匹配IPv4格式 |  | ||||||
| 		if (IPV4_PATTERN.matcher(trimmedIp).matches()) { |  | ||||||
| 			return IPV4; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// 判断是否匹配IPv6格式 |  | ||||||
| 		if (IPV6_PATTERN.matcher(trimmedIp).matches()) { |  | ||||||
| 			return IPV6; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// 不符合任一格式时抛出异常 |  | ||||||
| 		throw new NetworkException( |  | ||||||
| 			StringUtils.format("[{}] 不是有效的IPv4或IPv6地址", ip) |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将当前 NetworkAddress 转换为 InetAddress 对象。 |  | ||||||
| 	 * |  | ||||||
| 	 * @return InetAddress 对象 |  | ||||||
| 	 */ |  | ||||||
| 	public InetAddress toInetAddress() { |  | ||||||
| 		try { |  | ||||||
| 			return InetAddress.getByName(ip != null ? ip : domain); |  | ||||||
| 		} catch (UnknownHostException e) { |  | ||||||
| 			throw new RuntimeException(e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 返回 NetworkAddress 的字符串表示形式。 |  | ||||||
| 	 * |  | ||||||
| 	 * @return 字符串表示 |  | ||||||
| 	 */ |  | ||||||
| 	public String toString() { |  | ||||||
| 		return isdom |  | ||||||
| 			? StringUtils.format( |  | ||||||
| 				"NetworkAddress(IP='{}',type='{}'," + "domain='{}')", |  | ||||||
| 				ip, |  | ||||||
| 				IPv, |  | ||||||
| 				domain |  | ||||||
| 			) |  | ||||||
| 			: StringUtils.format("NetworkAddress(IP='{}',type='{}')", ip, IPv); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,164 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile NetworkEndpoint.java |  | ||||||
|  * LastUpdate 2025-09-14 22:12:16 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.network; |  | ||||||
| 
 |  | ||||||
| import com.mingliqiye.utils.string.StringUtils; |  | ||||||
| import lombok.Getter; |  | ||||||
| 
 |  | ||||||
| import java.io.Serializable; |  | ||||||
| import java.net.InetSocketAddress; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * IP和端口聚集类,用于封装网络地址与端口信息。 |  | ||||||
|  * 该类提供了与InetSocketAddress之间的相互转换功能。 |  | ||||||
|  * |  | ||||||
|  * @author MingLiPro |  | ||||||
|  * @see InetSocketAddress |  | ||||||
|  */ |  | ||||||
| public class NetworkEndpoint implements Serializable { |  | ||||||
| 
 |  | ||||||
| 	@Getter |  | ||||||
| 	private final NetworkAddress networkAddress; |  | ||||||
| 
 |  | ||||||
| 	@Getter |  | ||||||
| 	private final NetworkPort networkPort; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 构造函数,使用指定的网络地址和端口创建NetworkEndpoint实例。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param networkAddress 网络地址对象 |  | ||||||
| 	 * @param networkPort    网络端口对象 |  | ||||||
| 	 * @see NetworkAddress |  | ||||||
| 	 * @see NetworkPort |  | ||||||
| 	 */ |  | ||||||
| 	private NetworkEndpoint( |  | ||||||
| 		NetworkAddress networkAddress, |  | ||||||
| 		NetworkPort networkPort |  | ||||||
| 	) { |  | ||||||
| 		this.networkAddress = networkAddress; |  | ||||||
| 		this.networkPort = networkPort; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 根据给定的InetSocketAddress对象创建NetworkEndpoint实例。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param address InetSocketAddress对象 |  | ||||||
| 	 * @return 新建的NetworkEndpoint实例 |  | ||||||
| 	 * @see InetSocketAddress |  | ||||||
| 	 */ |  | ||||||
| 	public static NetworkEndpoint of(InetSocketAddress address) { |  | ||||||
| 		return new NetworkEndpoint( |  | ||||||
| 			new NetworkAddress(address.getHostString()), |  | ||||||
| 			new NetworkPort(address.getPort()) |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 根据主机名或IP字符串和端口号创建NetworkEndpoint实例。 |  | ||||||
| 	 * |  | ||||||
| 	 * @param s 主机名或IP地址字符串 |  | ||||||
| 	 * @param i 端口号 |  | ||||||
| 	 * @return 新建的NetworkEndpoint实例 |  | ||||||
| 	 */ |  | ||||||
| 	public static NetworkEndpoint of(String s, Integer i) { |  | ||||||
| 		NetworkAddress networkAddress = new NetworkAddress(s); |  | ||||||
| 		NetworkPort networkPort = new NetworkPort(i); |  | ||||||
| 		return new NetworkEndpoint(networkAddress, networkPort); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 根据"host:port"格式的字符串创建NetworkEndpoint实例。 |  | ||||||
| 	 * 例如:"127.0.0.1:8080" |  | ||||||
| 	 * |  | ||||||
| 	 * @param s "host:port"格式的字符串 |  | ||||||
| 	 * @return 新建的NetworkEndpoint实例 |  | ||||||
| 	 */ |  | ||||||
| 	public static NetworkEndpoint of(String s) { |  | ||||||
| 		// 查找最后一个冒号的位置,以支持IPv6地址中的冒号 |  | ||||||
| 		int lastColonIndex = s.lastIndexOf(':'); |  | ||||||
| 		return of( |  | ||||||
| 			s.substring(0, lastColonIndex), |  | ||||||
| 			Integer.parseInt(s.substring(lastColonIndex + 1)) |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将当前NetworkEndpoint转换为InetSocketAddress对象。 |  | ||||||
| 	 * |  | ||||||
| 	 * @return 对应的InetSocketAddress对象 |  | ||||||
| 	 * @see InetSocketAddress |  | ||||||
| 	 */ |  | ||||||
| 	public InetSocketAddress toInetSocketAddress() { |  | ||||||
| 		return new InetSocketAddress( |  | ||||||
| 			networkAddress.toInetAddress(), |  | ||||||
| 			networkPort.getPort() |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 将当前NetworkEndpoint转换为"host:port"格式的字符串。 |  | ||||||
| 	 * 例如:"127.0.0.1:25563" |  | ||||||
| 	 * |  | ||||||
| 	 * @return 格式化后的字符串 |  | ||||||
| 	 */ |  | ||||||
| 	public String toHostPortString() { |  | ||||||
| 		return StringUtils.format( |  | ||||||
| 			"{}:{}", |  | ||||||
| 			networkAddress.getIp(), |  | ||||||
| 			networkPort.getPort() |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 返回NetworkEndpoint的详细字符串表示形式。 |  | ||||||
| 	 * 格式:NetworkEndpoint(IP=...,Port=...,Endpoint=...) |  | ||||||
| 	 * |  | ||||||
| 	 * @return 包含详细信息的字符串 |  | ||||||
| 	 */ |  | ||||||
| 	public String toString() { |  | ||||||
| 		return StringUtils.format( |  | ||||||
| 			"NetworkEndpoint(IP={},Port={},Endpoint={})", |  | ||||||
| 			networkAddress.getIp(), |  | ||||||
| 			networkPort.getPort(), |  | ||||||
| 			toHostPortString() |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取主机名或IP地址字符串。 |  | ||||||
| 	 * |  | ||||||
| 	 * @return 主机名或IP地址 |  | ||||||
| 	 */ |  | ||||||
| 	public String getHost() { |  | ||||||
| 		return networkAddress.getIp(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取端口号。 |  | ||||||
| 	 * |  | ||||||
| 	 * @return 端口号 |  | ||||||
| 	 */ |  | ||||||
| 	public Integer getPort() { |  | ||||||
| 		return networkPort.getPort(); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -1,64 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile NetworkPort.java |  | ||||||
|  * LastUpdate 2025-09-14 22:12:16 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.network; |  | ||||||
| 
 |  | ||||||
| import com.mingliqiye.utils.string.StringUtils; |  | ||||||
| import lombok.Getter; |  | ||||||
| 
 |  | ||||||
| import java.io.Serializable; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 网络端口类 |  | ||||||
|  * |  | ||||||
|  * @author MingLiPro |  | ||||||
|  */ |  | ||||||
| public class NetworkPort implements Serializable { |  | ||||||
| 
 |  | ||||||
| 	@Getter |  | ||||||
| 	private final int port; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 构造函数,创建一个网络端口对象 |  | ||||||
| 	 * |  | ||||||
| 	 * @param port 端口号,必须在0-65535范围内 |  | ||||||
| 	 */ |  | ||||||
| 	public NetworkPort(int port) { |  | ||||||
| 		testPort(port); |  | ||||||
| 		this.port = port; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 验证端口号是否合法 |  | ||||||
| 	 * |  | ||||||
| 	 * @param port 待验证的端口号 |  | ||||||
| 	 * @throws NetworkException 当端口号不在合法范围(0-65535)内时抛出异常 |  | ||||||
| 	 */ |  | ||||||
| 	public static void testPort(int port) { |  | ||||||
| 		// 验证端口号范围是否在0-65535之间 |  | ||||||
| 		if (!(0 <= port && 65535 >= port)) { |  | ||||||
| 			throw new NetworkException( |  | ||||||
| 				StringUtils.format("{} 不是正确的端口号", port) |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @ -15,18 +15,20 @@ | |||||||
|  * |  * | ||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile PlayerSample.java |  * CurrentFile StreamEmptyException.java | ||||||
|  * LastUpdate 2025-09-09 08:37:33 |  * LastUpdate 2025-09-20 13:24:07 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.mingliqiye.utils.minecraft.slp; | package com.mingliqiye.utils.stream; | ||||||
| 
 | 
 | ||||||
| import lombok.Data; | public class StreamEmptyException extends java.lang.RuntimeException { | ||||||
| 
 | 
 | ||||||
| @Data |     public StreamEmptyException(String message) { | ||||||
| public class PlayerSample { |         super(message); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 	private String name; |     public StreamEmptyException(String message, Throwable cause) { | ||||||
| 	private String id; |         super(message, cause); | ||||||
|  |     } | ||||||
| } | } | ||||||
							
								
								
									
										1131
									
								
								src/main/java/com/mingliqiye/utils/stream/SuperStream.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1131
									
								
								src/main/java/com/mingliqiye/utils/stream/SuperStream.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -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-14 18:43:04 |  * 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) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | |||||||
							
								
								
									
										54
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base16.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base16.kt
									
									
									
									
									
										Normal 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() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										313
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base256.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base256.kt
									
									
									
									
									
										Normal 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 | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base64.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base64.kt
									
									
									
									
									
										Normal 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) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										404
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base91.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								src/main/kotlin/com/mingliqiye/utils/base/Base91.kt
									
									
									
									
									
										Normal 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) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										173
									
								
								src/main/kotlin/com/mingliqiye/utils/base/BaseCodec.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/main/kotlin/com/mingliqiye/utils/base/BaseCodec.kt
									
									
									
									
									
										Normal 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) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								src/main/kotlin/com/mingliqiye/utils/base/BaseUtils.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/main/kotlin/com/mingliqiye/utils/base/BaseUtils.kt
									
									
									
									
									
										Normal 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() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @ -1,160 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile Base64Utils.kt |  | ||||||
|  * LastUpdate 2025-09-14 18:44:22 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| @file:JvmName("Base64Utils") |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.base64 |  | ||||||
| 
 |  | ||||||
| import java.io.File |  | ||||||
| import java.io.IOException |  | ||||||
| import java.nio.file.Path |  | ||||||
| import java.util.* |  | ||||||
| 
 |  | ||||||
| val BASE_64_ENCODER: Base64.Encoder = Base64.getEncoder() |  | ||||||
| val BASE_64_DECODER: Base64.Decoder = Base64.getDecoder() |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将字节数组编码为Base64字符串 |  | ||||||
|  * |  | ||||||
|  * @param bytes 需要编码的字节数组 |  | ||||||
|  * @return 编码后的Base64字符串 |  | ||||||
|  */ |  | ||||||
| fun encode(bytes: ByteArray): String { |  | ||||||
|     return BASE_64_ENCODER.encodeToString(bytes) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将Base64字符串解码为字节数组 |  | ||||||
|  * |  | ||||||
|  * @param string 需要解码的Base64字符串 |  | ||||||
|  * @return 解码后的字节数组 |  | ||||||
|  */ |  | ||||||
| fun decode(string: String): ByteArray { |  | ||||||
|     return BASE_64_DECODER.decode(string) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将文件内容编码为Base64字符串 |  | ||||||
|  * |  | ||||||
|  * @param file 需要编码的文件 |  | ||||||
|  * @return 文件内容的Base64编码字符串 |  | ||||||
|  * @throws IOException 当文件读取失败时抛出 |  | ||||||
|  */ |  | ||||||
| @Throws(IOException::class) |  | ||||||
| fun encode(file: File): String { |  | ||||||
|     return encode(file.readBytes()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将Base64字符串解码并写入文件 |  | ||||||
|  * |  | ||||||
|  * @param file 目标文件 |  | ||||||
|  * @param string 需要解码的Base64字符串 |  | ||||||
|  * @throws IOException 当文件写入失败时抛出 |  | ||||||
|  */ |  | ||||||
| @Throws(IOException::class) |  | ||||||
| fun decode(file: File, string: String) { |  | ||||||
|     file.writeBytes(decode(string)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 安全地将文件内容编码为Base64字符串,出现异常时返回null |  | ||||||
|  * |  | ||||||
|  * @param file 需要编码的文件 |  | ||||||
|  * @return 文件内容的Base64编码字符串,失败时返回null |  | ||||||
|  */ |  | ||||||
| fun encodeSafe(file: File): String? { |  | ||||||
|     return try { |  | ||||||
|         encode(file) |  | ||||||
|     } catch (_: Exception) { |  | ||||||
|         null |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 安全地将Base64字符串解码并写入文件,返回操作是否成功 |  | ||||||
|  * |  | ||||||
|  * @param file 目标文件 |  | ||||||
|  * @param string 需要解码的Base64字符串 |  | ||||||
|  * @return 操作成功返回true,失败返回false |  | ||||||
|  */ |  | ||||||
| fun decodeSafe(file: File, string: String): Boolean { |  | ||||||
|     return try { |  | ||||||
|         decode(file, string) |  | ||||||
|         true |  | ||||||
|     } catch (_: Exception) { |  | ||||||
|         false |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将路径对应的文件内容编码为Base64字符串 |  | ||||||
|  * |  | ||||||
|  * @param path 需要编码的文件路径 |  | ||||||
|  * @return 文件内容的Base64编码字符串 |  | ||||||
|  * @throws IOException 当文件读取失败时抛出 |  | ||||||
|  */ |  | ||||||
| @Throws(IOException::class) |  | ||||||
| fun encode(path: Path): String { |  | ||||||
|     return encode(path.toFile().readBytes()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将Base64字符串解码并写入路径指定的文件 |  | ||||||
|  * |  | ||||||
|  * @param path 目标文件路径 |  | ||||||
|  * @param string 需要解码的Base64字符串 |  | ||||||
|  * @throws IOException 当文件写入失败时抛出 |  | ||||||
|  */ |  | ||||||
| @Throws(IOException::class) |  | ||||||
| fun decode(path: Path, string: String) { |  | ||||||
|     path.toFile().writeBytes(decode(string)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 安全地将路径对应的文件内容编码为Base64字符串,出现异常时返回null |  | ||||||
|  * |  | ||||||
|  * @param path 需要编码的文件路径 |  | ||||||
|  * @return 文件内容的Base64编码字符串,失败时返回null |  | ||||||
|  */ |  | ||||||
| fun encodeSafe(path: Path): String? { |  | ||||||
|     return try { |  | ||||||
|         encode(path) |  | ||||||
|     } catch (_: Exception) { |  | ||||||
|         null |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 安全地将Base64字符串解码并写入路径指定的文件,返回操作是否成功 |  | ||||||
|  * |  | ||||||
|  * @param path 目标文件路径 |  | ||||||
|  * @param string 需要解码的Base64字符串 |  | ||||||
|  * @return 操作成功返回true,失败返回false |  | ||||||
|  */ |  | ||||||
| fun decodeSafe(path: Path, string: String): Boolean { |  | ||||||
|     return try { |  | ||||||
|         decode(path, string) |  | ||||||
|         true |  | ||||||
|     } catch (_: Exception) { |  | ||||||
|         false |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										59
									
								
								src/main/kotlin/com/mingliqiye/utils/bcrypt/BCrypt.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/main/kotlin/com/mingliqiye/utils/bcrypt/BCrypt.kt
									
									
									
									
									
										Normal 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) | ||||||
|  | } | ||||||
| @ -1,241 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile Factory.kt |  | ||||||
|  * LastUpdate 2025-09-14 19:09:28 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| @file:JvmName("Factory") |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.bean |  | ||||||
| 
 |  | ||||||
| import com.mingliqiye.utils.bean.annotation.ComponentBean |  | ||||||
| import com.mingliqiye.utils.bean.annotation.InjectBean |  | ||||||
| import java.io.File |  | ||||||
| import java.net.URL |  | ||||||
| import java.util.* |  | ||||||
| import java.util.concurrent.ConcurrentHashMap |  | ||||||
| import kotlin.reflect.KClass |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 存储所有已注册的Bean实例,键为Bean名称,值为Bean实例 |  | ||||||
|  */ |  | ||||||
| private val BEANS: ConcurrentHashMap<String, Any> = ConcurrentHashMap() |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 存储按类型查找的Bean实例,键为Bean的Class对象,值为Bean实例 |  | ||||||
|  */ |  | ||||||
| private val TYPE_BEANS: ConcurrentHashMap<KClass<*>, Any> = ConcurrentHashMap() |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 自动扫描指定类所在包下的所有类并注册为Bean |  | ||||||
|  * |  | ||||||
|  * @param c 指定的类,用于获取其所在的包 |  | ||||||
|  * @throws IllegalArgumentException 如果传入的类为null或位于默认包中 |  | ||||||
|  */ |  | ||||||
| fun autoScan(c: Class<*>?) { |  | ||||||
|     if (c == null) { |  | ||||||
|         throw IllegalArgumentException("Class cannot be null") |  | ||||||
|     } |  | ||||||
|     val pkg = c.`package` |  | ||||||
|     if (pkg == null) { |  | ||||||
|         throw IllegalArgumentException("Class is in the default package") |  | ||||||
|     } |  | ||||||
|     scan(pkg.name) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 扫描指定包路径下的所有类文件,并注册其中带有@ComponentBean注解的类为Bean |  | ||||||
|  * |  | ||||||
|  * @param basePackage 要扫描的基础包名 |  | ||||||
|  * @throws RuntimeException 如果在扫描过程中发生异常 |  | ||||||
|  */ |  | ||||||
| fun scan(basePackage: String) { |  | ||||||
|     try { |  | ||||||
|         val path = basePackage.replace('.', '/') |  | ||||||
|         val classLoader = Thread.currentThread().contextClassLoader |  | ||||||
|         val resources: Enumeration<URL> = classLoader.getResources(path) |  | ||||||
|         while (resources.hasMoreElements()) { |  | ||||||
|             val resource = resources.nextElement() |  | ||||||
|             val file = File(resource.toURI()) |  | ||||||
|             scanDirectory(file, basePackage) |  | ||||||
|         } |  | ||||||
|         injectDependencies() |  | ||||||
|     } catch (e: Exception) { |  | ||||||
|         throw RuntimeException(e) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 递归扫描目录中的所有类文件,并注册符合条件的类为Bean |  | ||||||
|  * |  | ||||||
|  * @param directory   当前要扫描的目录 |  | ||||||
|  * @param packageName 当前目录对应的包名 |  | ||||||
|  * @throws Exception 如果在扫描或类加载过程中发生异常 |  | ||||||
|  */ |  | ||||||
| private fun scanDirectory(directory: File, packageName: String) { |  | ||||||
|     val files = directory.listFiles() ?: return |  | ||||||
| 
 |  | ||||||
|     for (file in files) { |  | ||||||
|         if (file.isDirectory) { |  | ||||||
|             scanDirectory(file, "$packageName.${file.name}") |  | ||||||
|         } else if (file.name.endsWith(".class")) { |  | ||||||
|             val className = packageName + '.' + file.name.replace(".class", "") |  | ||||||
|             registerComponent(Class.forName(className)) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 注册一个带有@ComponentBean注解的类为Bean实例 |  | ||||||
|  * |  | ||||||
|  * @param clazz 要注册的类 |  | ||||||
|  * @throws Exception 如果在实例化类或处理注解时发生异常 |  | ||||||
|  */ |  | ||||||
| private fun registerComponent(clazz: Class<*>) { |  | ||||||
|     if (clazz.isAnnotationPresent(ComponentBean::class.java)) { |  | ||||||
|         val component = clazz.getAnnotation(ComponentBean::class.java) |  | ||||||
|         val name = component.value.ifEmpty { clazz.name } |  | ||||||
|         val instance = clazz.getDeclaredConstructor().newInstance() |  | ||||||
|         BEANS[name] = instance |  | ||||||
|         val kClass = clazz.kotlin |  | ||||||
|         TYPE_BEANS[kClass] = instance |  | ||||||
| 
 |  | ||||||
|         for (interfaceClass in clazz.interfaces) { |  | ||||||
|             TYPE_BEANS.putIfAbsent(interfaceClass.kotlin, instance) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 对所有已注册的Bean进行依赖注入处理 |  | ||||||
|  * |  | ||||||
|  * @throws Exception 如果在注入过程中发生异常 |  | ||||||
|  */ |  | ||||||
| private fun injectDependencies() { |  | ||||||
|     for (bean in BEANS.values) { |  | ||||||
|         for (field in bean.javaClass.declaredFields) { |  | ||||||
|             if (field.isAnnotationPresent(InjectBean::class.java)) { |  | ||||||
|                 val inject = field.getAnnotation(InjectBean::class.java) |  | ||||||
|                 val dependency = findDependency(field.type, inject.value) |  | ||||||
|                 if (dependency == null) { |  | ||||||
|                     throw IllegalStateException( |  | ||||||
|                         "No suitable dependency found for field " + field.name + " in class " + bean.javaClass.name |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|                 field.isAccessible = true |  | ||||||
|                 field.set(bean, dependency) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 根据类型和名称查找对应的依赖实例 |  | ||||||
|  * |  | ||||||
|  * @param type 依赖的类型 |  | ||||||
|  * @param name 依赖的名称(可为空) |  | ||||||
|  * @return 找到的依赖实例,未找到则返回null |  | ||||||
|  */ |  | ||||||
| private fun findDependency(type: Class<*>, name: String): Any? { |  | ||||||
|     if (name.isNotEmpty()) { |  | ||||||
|         return BEANS[name] |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     val dependency = TYPE_BEANS[type.kotlin] |  | ||||||
|     if (dependency != null) { |  | ||||||
|         return dependency |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for (interfaceType in TYPE_BEANS.keys) { |  | ||||||
|         if (type.isAssignableFrom(interfaceType.java)) { |  | ||||||
|             return TYPE_BEANS[interfaceType] |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return null |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将一个对象添加到Bean容器中,使用其类名作为键 |  | ||||||
|  * |  | ||||||
|  * @param obj 要添加的对象 |  | ||||||
|  * @throws RuntimeException 如果在注入依赖时发生异常 |  | ||||||
|  */ |  | ||||||
| fun add(obj: Any) { |  | ||||||
|     val clazz = obj.javaClass |  | ||||||
|     val name = clazz.name |  | ||||||
|     BEANS[name] = obj |  | ||||||
|     TYPE_BEANS[clazz.kotlin] = obj |  | ||||||
|     try { |  | ||||||
|         injectDependencies() |  | ||||||
|     } catch (e: Exception) { |  | ||||||
|         throw RuntimeException(e) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 将一个对象以指定名称添加到Bean容器中 |  | ||||||
|  * |  | ||||||
|  * @param name   Bean的名称 |  | ||||||
|  * @param obj 要添加的对象 |  | ||||||
|  * @throws RuntimeException 如果在注入依赖时发生异常 |  | ||||||
|  */ |  | ||||||
| fun add(name: String, obj: Any) { |  | ||||||
|     BEANS[name] = obj |  | ||||||
|     TYPE_BEANS[obj.javaClass.kotlin] = obj |  | ||||||
|     try { |  | ||||||
|         injectDependencies() |  | ||||||
|     } catch (e: Exception) { |  | ||||||
|         throw RuntimeException(e) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 根据类型获取对应的Bean实例 |  | ||||||
|  * |  | ||||||
|  * @param objclass Bean的类型 |  | ||||||
|  * @param T      Bean的泛型类型 |  | ||||||
|  * @return 对应类型的Bean实例,未找到则返回null |  | ||||||
|  */ |  | ||||||
| @Suppress("UNCHECKED_CAST") |  | ||||||
| fun <T : Any> get(objclass: KClass<T>): T? { |  | ||||||
|     return TYPE_BEANS[objclass] as? T |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 根据名称和类型获取对应的Bean实例 |  | ||||||
|  * |  | ||||||
|  * @param name     Bean的名称 |  | ||||||
|  * @param objclass Bean的类型 |  | ||||||
|  * @param T      Bean的泛型类型 |  | ||||||
|  * @return 对应名称和类型的Bean实例,未找到则返回null |  | ||||||
|  */ |  | ||||||
| @Suppress("UNCHECKED_CAST") |  | ||||||
| fun <T : Any> get(name: String, objclass: KClass<T>): T? { |  | ||||||
|     return BEANS[name] as? T |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 根据名称获取对应的Bean实例 |  | ||||||
|  * |  | ||||||
|  * @param name Bean的名称 |  | ||||||
|  * @return 对应名称的Bean实例,未找到则返回null |  | ||||||
|  */ |  | ||||||
| fun get(name: String): Any? { |  | ||||||
|     return BEANS[name] |  | ||||||
| } |  | ||||||
| @ -1,43 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2025 mingliqiye |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  * |  | ||||||
|  * ProjectName mingli-utils |  | ||||||
|  * ModuleName mingli-utils.main |  | ||||||
|  * CurrentFile ComponentBean.kt |  | ||||||
|  * LastUpdate 2025-09-14 18:48:59 |  | ||||||
|  * UpdateUser MingLiPro |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.mingliqiye.utils.bean.annotation |  | ||||||
| 
 |  | ||||||
| import kotlin.annotation.AnnotationRetention.RUNTIME |  | ||||||
| import kotlin.annotation.AnnotationTarget.CLASS |  | ||||||
| import kotlin.annotation.AnnotationTarget.FIELD |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 组件bean注解 |  | ||||||
|  * @author MingLiPro |  | ||||||
|  */ |  | ||||||
| @Retention(RUNTIME) |  | ||||||
| @Target(CLASS) |  | ||||||
| annotation class ComponentBean(val value: String = "") |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 注入bean注解 |  | ||||||
|  * @author MingLiPro |  | ||||||
|  */ |  | ||||||
| @Retention(RUNTIME) |  | ||||||
| @Target(FIELD) |  | ||||||
| annotation class InjectBean(val value: String = "") |  | ||||||
| @ -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() | ||||||
|  | } | ||||||
|  | |||||||
| @ -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 |  | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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) | ||||||
|  | } | ||||||
|  | |||||||
| @ -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 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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 | ||||||
|  |         } | ||||||
|  | } | ||||||
							
								
								
									
										146
									
								
								src/main/kotlin/com/mingliqiye/utils/dto/DtoUtils.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/main/kotlin/com/mingliqiye/utils/dto/DtoUtils.kt
									
									
									
									
									
										Normal 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 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @ -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) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										63
									
								
								src/main/kotlin/com/mingliqiye/utils/io/IO.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/main/kotlin/com/mingliqiye/utils/io/IO.kt
									
									
									
									
									
										Normal 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) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -16,7 +16,7 @@ | |||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile FieldStructure.kt |  * CurrentFile FieldStructure.kt | ||||||
|  * LastUpdate 2025-09-14 18:19:29 |  * LastUpdate 2025-09-15 22:32:50 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										231
									
								
								src/main/kotlin/com/mingliqiye/utils/json/GsonJsonApi.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/main/kotlin/com/mingliqiye/utils/json/GsonJsonApi.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,231 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2025 mingliqiye | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  * ProjectName mingli-utils | ||||||
|  |  * ModuleName mingli-utils.main | ||||||
|  |  * CurrentFile GsonJsonApi.kt | ||||||
|  |  * LastUpdate 2025-09-15 22:07:43 | ||||||
|  |  * UpdateUser MingLiPro | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.mingliqiye.utils.json | ||||||
|  | 
 | ||||||
|  | import com.google.gson.* | ||||||
|  | import com.mingliqiye.utils.json.converters.JsonConverter | ||||||
|  | import com.mingliqiye.utils.json.converters.JsonStringConverter | ||||||
|  | 
 | ||||||
|  | class GsonJsonApi : JsonApi { | ||||||
|  | 
 | ||||||
|  |     private var gsonUnicode: Gson | ||||||
|  |     private var gsonPretty: Gson | ||||||
|  |     private var gsonPrettyUnicode: Gson | ||||||
|  |     private var gson: Gson | ||||||
|  | 
 | ||||||
|  |     constructor() { | ||||||
|  |         gson = GsonBuilder() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  | 
 | ||||||
|  |         gsonUnicode = GsonBuilder() | ||||||
|  |             .disableHtmlEscaping() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  | 
 | ||||||
|  |         gsonPretty = GsonBuilder() | ||||||
|  |             .setPrettyPrinting() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  | 
 | ||||||
|  |         gsonPrettyUnicode = GsonBuilder() | ||||||
|  |             .setPrettyPrinting() | ||||||
|  |             .disableHtmlEscaping() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     constructor(gson: Gson) { | ||||||
|  |         this.gson = gson | ||||||
|  |             .newBuilder() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  |         this.gsonUnicode = gson | ||||||
|  |             .newBuilder() | ||||||
|  |             .disableHtmlEscaping() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  |         this.gsonPretty = gson | ||||||
|  |             .newBuilder() | ||||||
|  |             .setPrettyPrinting() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  |         this.gsonPrettyUnicode = gson | ||||||
|  |             .newBuilder() | ||||||
|  |             .setPrettyPrinting() | ||||||
|  |             .disableHtmlEscaping() | ||||||
|  |             .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||||||
|  |             .create() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun <T> parse(json: String, clazz: Class<T>): T { | ||||||
|  |         return gson.fromJson(json, clazz) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun <T> parse(json: String, type: JsonTypeReference<T>): T { | ||||||
|  |         return gson.fromJson(json, type.type) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun format(obj: Any): String { | ||||||
|  |         return gson.toJson(obj) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun formatUnicode(obj: Any): String { | ||||||
|  |         return gsonUnicode.toJson(obj) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun formatPretty(obj: Any): String { | ||||||
|  |         return gsonPretty.toJson(obj) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun formatPrettyUnicode(obj: Any): String { | ||||||
|  |         return gsonPrettyUnicode.toJson(obj) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun isValidJson(json: String): Boolean { | ||||||
|  |         return try { | ||||||
|  |             JsonParser.parseString(json) | ||||||
|  |             true | ||||||
|  |         } catch (e: JsonSyntaxException) { | ||||||
|  |             false | ||||||
|  |         } catch (e: Exception) { | ||||||
|  |             false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun merge(vararg jsons: String): String { | ||||||
|  |         val merged = JsonObject() | ||||||
|  |         for (json in jsons) { | ||||||
|  |             if (json.isNullOrEmpty()) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             try { | ||||||
|  |                 val obj = JsonParser.parseString(json).asJsonObject | ||||||
|  |                 for (key in obj.keySet()) { | ||||||
|  |                     merged.add(key, obj.get(key)) | ||||||
|  |                 } | ||||||
|  |             } catch (e: Exception) { | ||||||
|  |                 // 忽略无效的 JSON 字符串 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return gson.toJson(merged) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun getNodeValue(json: String, path: String): String? { | ||||||
|  |         return try { | ||||||
|  |             var element = JsonParser.parseString(json) | ||||||
|  |             val paths = path.split("\\.".toRegex()).toTypedArray() | ||||||
|  |             var current = element | ||||||
|  | 
 | ||||||
|  |             for (p in paths) { | ||||||
|  |                 if (current.isJsonObject) { | ||||||
|  |                     current = current.asJsonObject.get(p) | ||||||
|  |                 } else { | ||||||
|  |                     return null | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (current == null) { | ||||||
|  |                     return null | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (current.isJsonPrimitive) current.asString else current.toString() | ||||||
|  |         } catch (e: Exception) { | ||||||
|  |             null | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun updateNodeValue(json: String, path: String, newValue: Any): String { | ||||||
|  |         return try { | ||||||
|  |             val obj = JsonParser.parseString(json).asJsonObject | ||||||
|  |             val paths = path.split("\\.".toRegex()).toTypedArray() | ||||||
|  |             var current = obj | ||||||
|  | 
 | ||||||
|  |             // 导航到倒数第二层 | ||||||
|  |             for (i in 0 until paths.size - 1) { | ||||||
|  |                 val p = paths[i] | ||||||
|  |                 if (!current.has(p) || !current.get(p).isJsonObject) { | ||||||
|  |                     current.add(p, JsonObject()) | ||||||
|  |                 } | ||||||
|  |                 current = current.getAsJsonObject(p) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // 设置最后一层的值 | ||||||
|  |             val lastPath = paths[paths.size - 1] | ||||||
|  |             val element = gson.toJsonTree(newValue) | ||||||
|  |             current.add(lastPath, element) | ||||||
|  | 
 | ||||||
|  |             gson.toJson(obj) | ||||||
|  |         } catch (e: Exception) { | ||||||
|  |             json | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun <T, D> convert(source: T, destinationClass: Class<D>): D { | ||||||
|  |         val json = gson.toJson(source) | ||||||
|  |         return gson.fromJson(json, destinationClass) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D { | ||||||
|  |         val json = gson.toJson(source) | ||||||
|  |         return gson.fromJson(json, destinationType.type) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun addJsonConverter(c: JsonConverter<*, *>) { | ||||||
|  |         c.getStringConverter()?.let { | ||||||
|  |             gson = gson | ||||||
|  |                 .newBuilder() | ||||||
|  |                 .registerTypeAdapter( | ||||||
|  |                     it.tClass, | ||||||
|  |                     it.gsonJsonStringConverterAdapter | ||||||
|  |                 ) | ||||||
|  |                 .create() | ||||||
|  |             gsonUnicode = gsonUnicode | ||||||
|  |                 .newBuilder() | ||||||
|  |                 .registerTypeAdapter( | ||||||
|  |                     it.tClass, | ||||||
|  |                     it.gsonJsonStringConverterAdapter | ||||||
|  |                 ) | ||||||
|  |                 .create() | ||||||
|  |             gsonPretty = gsonPretty | ||||||
|  |                 .newBuilder() | ||||||
|  |                 .registerTypeAdapter( | ||||||
|  |                     it.tClass, | ||||||
|  |                     it.gsonJsonStringConverterAdapter | ||||||
|  |                 ) | ||||||
|  |                 .create() | ||||||
|  |             gsonPrettyUnicode = gsonPrettyUnicode | ||||||
|  |                 .newBuilder() | ||||||
|  |                 .registerTypeAdapter( | ||||||
|  |                     it.tClass, | ||||||
|  |                     it.gsonJsonStringConverterAdapter | ||||||
|  |                 ) | ||||||
|  |                 .create() | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun addJsonStringConverter(c: JsonStringConverter<*>) { | ||||||
|  |         addJsonConverter(c) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										301
									
								
								src/main/kotlin/com/mingliqiye/utils/json/JacksonJsonApi.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								src/main/kotlin/com/mingliqiye/utils/json/JacksonJsonApi.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2025 mingliqiye | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  * ProjectName mingli-utils | ||||||
|  |  * ModuleName mingli-utils.main | ||||||
|  |  * CurrentFile JacksonJsonApi.kt | ||||||
|  |  * LastUpdate 2025-09-15 22:07:43 | ||||||
|  |  * UpdateUser MingLiPro | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.mingliqiye.utils.json | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.core.JsonGenerator | ||||||
|  | import com.fasterxml.jackson.core.JsonProcessingException | ||||||
|  | import com.fasterxml.jackson.databind.JsonNode | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper | ||||||
|  | import com.fasterxml.jackson.databind.ObjectReader | ||||||
|  | import com.fasterxml.jackson.databind.node.ObjectNode | ||||||
|  | import com.mingliqiye.utils.json.converters.JsonConverter | ||||||
|  | import com.mingliqiye.utils.json.converters.JsonStringConverter | ||||||
|  | import java.io.IOException | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 基于Jackson的JSON处理实现类,提供JSON字符串解析、格式化、合并、节点操作等功能。 | ||||||
|  |  */ | ||||||
|  | class JacksonJsonApi : JsonApi { | ||||||
|  | 
 | ||||||
|  |     private val objectMapper: ObjectMapper | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 使用默认的ObjectMapper构造实例 | ||||||
|  |      */ | ||||||
|  |     constructor() { | ||||||
|  |         this.objectMapper = ObjectMapper() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 使用指定的ObjectMapper构造实例 | ||||||
|  |      * | ||||||
|  |      * @param objectMapper 自定义的ObjectMapper实例 | ||||||
|  |      */ | ||||||
|  |     constructor(objectMapper: ObjectMapper) { | ||||||
|  |         this.objectMapper = objectMapper.copy() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将JSON字符串解析为指定类型的对象 | ||||||
|  |      * | ||||||
|  |      * @param json  待解析的JSON字符串 | ||||||
|  |      * @param clazz 目标对象类型 | ||||||
|  |      * @param <T>   泛型参数,表示目标对象类型 | ||||||
|  |      * @return 解析后的对象 | ||||||
|  |      * @throws JsonException 当解析失败时抛出异常 | ||||||
|  |      */ | ||||||
|  |     override fun <T> parse(json: String, clazz: Class<T>): T { | ||||||
|  |         return try { | ||||||
|  |             objectMapper.readValue(json, clazz) | ||||||
|  |         } catch (e: IOException) { | ||||||
|  |             throw JsonException("Failed to parse JSON string", e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将JSON字符串解析为复杂泛型结构的对象(如List、Map等) | ||||||
|  |      * | ||||||
|  |      * @param json JSON字符串 | ||||||
|  |      * @param type 泛型类型引用 | ||||||
|  |      * @param <T>  泛型参数,表示目标对象类型 | ||||||
|  |      * @return 解析后的对象 | ||||||
|  |      * @throws JsonException 当解析失败时抛出异常 | ||||||
|  |      */ | ||||||
|  |     override fun <T> parse(json: String, type: JsonTypeReference<T>): T { | ||||||
|  |         return try { | ||||||
|  |             val reader: ObjectReader = objectMapper.readerFor( | ||||||
|  |                 objectMapper.constructType(type.type) | ||||||
|  |             ) | ||||||
|  |             reader.readValue(json) | ||||||
|  |         } catch (e: IOException) { | ||||||
|  |             throw JsonException("Failed to parse JSON string", e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将对象格式化为JSON字符串 | ||||||
|  |      * | ||||||
|  |      * @param `object` 待格式化的对象 | ||||||
|  |      * @return 格式化后的JSON字符串 | ||||||
|  |      * @throws JsonException 当格式化失败时抛出异常 | ||||||
|  |      */ | ||||||
|  |     override fun format(obj: Any): String { | ||||||
|  |         return try { | ||||||
|  |             objectMapper.writeValueAsString(obj) | ||||||
|  |         } catch (e: JsonProcessingException) { | ||||||
|  |             throw JsonException( | ||||||
|  |                 "Failed to format object to JSON string", | ||||||
|  |                 e | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun formatUnicode(obj: Any): String { | ||||||
|  |         return try { | ||||||
|  |             objectMapper | ||||||
|  |                 .writer() | ||||||
|  |                 .with(JsonGenerator.Feature.ESCAPE_NON_ASCII) | ||||||
|  |                 .writeValueAsString(obj) | ||||||
|  |         } catch (e: JsonProcessingException) { | ||||||
|  |             throw JsonException(e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将对象格式化为美化(带缩进)的JSON字符串 | ||||||
|  |      * | ||||||
|  |      * @param `object` 待格式化的对象 | ||||||
|  |      * @return 美化后的JSON字符串 | ||||||
|  |      * @throws JsonException 当格式化失败时抛出异常 | ||||||
|  |      */ | ||||||
|  |     override fun formatPretty(obj: Any): String { | ||||||
|  |         return try { | ||||||
|  |             objectMapper | ||||||
|  |                 .writerWithDefaultPrettyPrinter() | ||||||
|  |                 .writeValueAsString(obj) | ||||||
|  |         } catch (e: JsonProcessingException) { | ||||||
|  |             throw JsonException( | ||||||
|  |                 "Failed to format object to pretty JSON string", | ||||||
|  |                 e | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun formatPrettyUnicode(obj: Any): String { | ||||||
|  |         return try { | ||||||
|  |             objectMapper | ||||||
|  |                 .writerWithDefaultPrettyPrinter() | ||||||
|  |                 .with(JsonGenerator.Feature.ESCAPE_NON_ASCII) | ||||||
|  |                 .writeValueAsString(obj) | ||||||
|  |         } catch (e: JsonProcessingException) { | ||||||
|  |             throw JsonException( | ||||||
|  |                 "Failed to format object to pretty JSON string", | ||||||
|  |                 e | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 判断给定字符串是否是有效的JSON格式 | ||||||
|  |      * | ||||||
|  |      * @param json 待验证的字符串 | ||||||
|  |      * @return 如果是有效JSON返回true,否则返回false | ||||||
|  |      */ | ||||||
|  |     override fun isValidJson(json: String): Boolean { | ||||||
|  |         return try { | ||||||
|  |             objectMapper.readTree(json) | ||||||
|  |             true | ||||||
|  |         } catch (e: Exception) { | ||||||
|  |             false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 合并多个JSON字符串为一个JSON对象 | ||||||
|  |      * | ||||||
|  |      * @param jsons 多个JSON字符串 | ||||||
|  |      * @return 合并后的JSON字符串 | ||||||
|  |      * @throws JsonException 当合并失败时抛出异常 | ||||||
|  |      */ | ||||||
|  |     override fun merge(vararg jsons: String): String { | ||||||
|  |         val result: ObjectNode = objectMapper.createObjectNode() | ||||||
|  |         for (json in jsons) { | ||||||
|  |             try { | ||||||
|  |                 val node: JsonNode = objectMapper.readTree(json) | ||||||
|  |                 if (node.isObject) { | ||||||
|  |                     result.setAll<JsonNode>(node as ObjectNode) | ||||||
|  |                 } | ||||||
|  |             } catch (e: IOException) { | ||||||
|  |                 // 忽略无效的JSON字符串 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return try { | ||||||
|  |             objectMapper.writeValueAsString(result) | ||||||
|  |         } catch (e: JsonProcessingException) { | ||||||
|  |             throw JsonException("Failed to merge JSON strings", e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取JSON字符串中指定路径的节点值 | ||||||
|  |      * | ||||||
|  |      * @param json JSON字符串 | ||||||
|  |      * @param path 节点路径,使用"."分隔 | ||||||
|  |      * @return 节点值的文本表示,如果路径不存在则返回null | ||||||
|  |      * @throws JsonException 当获取节点值失败时抛出异常 | ||||||
|  |      */ | ||||||
|  |     override fun getNodeValue(json: String, path: String): String? { | ||||||
|  |         return try { | ||||||
|  |             var node: JsonNode = objectMapper.readTree(json) | ||||||
|  |             val paths: Array<String> = path.split("\\.".toRegex()).toTypedArray() | ||||||
|  |             for (p in paths) { | ||||||
|  |                 node = node.get(p) | ||||||
|  |             } | ||||||
|  |             node.asText() | ||||||
|  |         } catch (e: IOException) { | ||||||
|  |             throw JsonException("Failed to get node value", e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 更新JSON字符串中指定路径的节点值 | ||||||
|  |      * | ||||||
|  |      * @param json     JSON字符串 | ||||||
|  |      * @param path     节点路径,使用"."分隔 | ||||||
|  |      * @param newValue 新的节点值 | ||||||
|  |      * @return 更新后的JSON字符串 | ||||||
|  |      * @throws JsonException 当更新节点值失败时抛出异常 | ||||||
|  |      */ | ||||||
|  |     override fun updateNodeValue(json: String, path: String, newValue: Any): String { | ||||||
|  |         return try { | ||||||
|  |             val node: JsonNode = objectMapper.readTree(json) | ||||||
|  |             if (node is ObjectNode) { | ||||||
|  |                 val objectNode: ObjectNode = node | ||||||
|  |                 val paths: Array<String> = path.split("\\.".toRegex()).toTypedArray() | ||||||
|  |                 var current: JsonNode = objectNode | ||||||
|  | 
 | ||||||
|  |                 // 导航到目标节点的父节点 | ||||||
|  |                 for (i in 0 until paths.size - 1) { | ||||||
|  |                     current = current.get(paths[i]) | ||||||
|  |                     if (current !is ObjectNode) { | ||||||
|  |                         return json // 路径不存在或无效 | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // 更新值 | ||||||
|  |                 if (current is ObjectNode) { | ||||||
|  |                     val parent: ObjectNode = current | ||||||
|  |                     parent.set<JsonNode>( | ||||||
|  |                         paths[paths.size - 1], | ||||||
|  |                         objectMapper.valueToTree(newValue) | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 objectMapper.writeValueAsString(objectNode) | ||||||
|  |             } | ||||||
|  |             json | ||||||
|  |         } catch (e: IOException) { | ||||||
|  |             throw JsonException("Failed to update node value", e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 在不同对象类型之间进行转换 | ||||||
|  |      * | ||||||
|  |      * @param source           源对象 | ||||||
|  |      * @param destinationClass 目标对象类型 | ||||||
|  |      * @param <T>              源对象类型 | ||||||
|  |      * @param <D>              目标对象类型 | ||||||
|  |      * @return 转换后的对象 | ||||||
|  |      */ | ||||||
|  |     override fun <T, D> convert(source: T, destinationClass: Class<D>): D { | ||||||
|  |         return objectMapper.convertValue(source, destinationClass) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 在不同泛型对象类型之间进行转换 | ||||||
|  |      * | ||||||
|  |      * @param source          源对象 | ||||||
|  |      * @param destinationType 目标对象的泛型类型引用 | ||||||
|  |      * @param <T>             源对象类型 | ||||||
|  |      * @param <D>             目标对象类型 | ||||||
|  |      * @return 转换后的对象 | ||||||
|  |      */ | ||||||
|  |     override fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D { | ||||||
|  |         return objectMapper.convertValue( | ||||||
|  |             source, | ||||||
|  |             objectMapper.constructType(destinationType.type) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun addJsonConverter(c: JsonConverter<*, *>) { | ||||||
|  |         c.getStringConverter()?.let { | ||||||
|  |             objectMapper.registerModule(it.jacksonJsonStringConverterAdapter.jacksonModule) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun addJsonStringConverter(c: JsonStringConverter<*>) { | ||||||
|  |         addJsonConverter(c) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -16,7 +16,7 @@ | |||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile JsonApi.kt |  * CurrentFile JsonApi.kt | ||||||
|  * LastUpdate 2025-09-15 11:10:59 |  * LastUpdate 2025-09-15 22:32:50 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| @ -306,8 +306,8 @@ interface JsonApi { | |||||||
|      * @param <T>         泛型参数,表示List中元素的类型 |      * @param <T>         泛型参数,表示List中元素的类型 | ||||||
|      * @return 解析后的List集合 |      * @return 解析后的List集合 | ||||||
|     </T> */ |     </T> */ | ||||||
|     fun <T> parseList(json: String, elementType: Class<T>): MutableList<T> { |     fun <T> parseList(json: String, elementType: Class<T>): List<T> { | ||||||
|         return parse<MutableList<T>>(json, JsonTypeUtils.listType<T>(elementType)) |         return parse(json, type = listType(elementType)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -372,7 +372,7 @@ interface JsonApi { | |||||||
|      * @param path 节点路径(如:"user.name") |      * @param path 节点路径(如:"user.name") | ||||||
|      * @return 节点值的字符串表示 |      * @return 节点值的字符串表示 | ||||||
|      */ |      */ | ||||||
|     fun getNodeValue(json: String, path: String): String |     fun getNodeValue(json: String, path: String): String? | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 更新JSON字符串中指定路径节点的值 |      * 更新JSON字符串中指定路径节点的值 | ||||||
|  | |||||||
| @ -15,19 +15,18 @@ | |||||||
|  * |  * | ||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile Players.java |  * CurrentFile JsonException.kt | ||||||
|  * LastUpdate 2025-09-09 08:37:33 |  * LastUpdate 2025-09-15 22:32:50 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.mingliqiye.utils.minecraft.slp; | package com.mingliqiye.utils.json | ||||||
| 
 | 
 | ||||||
| import lombok.Data; | class JsonException : RuntimeException { | ||||||
| 
 | 
 | ||||||
| @Data |     constructor(message: String) : super(message) | ||||||
| public class Players { |  | ||||||
| 
 | 
 | ||||||
| 	private int max; |     constructor(message: String, cause: Throwable) : super(message, cause) | ||||||
| 	private int online; | 
 | ||||||
| 	private PlayerSample[] sample; |     constructor(cause: Throwable) : this(cause.message ?: "", cause) | ||||||
| } | } | ||||||
							
								
								
									
										164
									
								
								src/main/kotlin/com/mingliqiye/utils/json/JsonTypeReference.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								src/main/kotlin/com/mingliqiye/utils/json/JsonTypeReference.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2025 mingliqiye | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  * ProjectName mingli-utils | ||||||
|  |  * ModuleName mingli-utils.main | ||||||
|  |  * CurrentFile JsonTypeReference.kt | ||||||
|  |  * LastUpdate 2025-09-15 22:32:50 | ||||||
|  |  * UpdateUser MingLiPro | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.mingliqiye.utils.json | ||||||
|  | 
 | ||||||
|  | import java.lang.reflect.ParameterizedType | ||||||
|  | import java.lang.reflect.Type | ||||||
|  | import java.util.* | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 通用的 JSON 类型引用类,用于在运行时保留泛型类型信息 | ||||||
|  |  * 适用于所有 JSON 库(Jackson、Gson、Fastjson 等) | ||||||
|  |  * | ||||||
|  |  * @param <T> 引用的泛型类型 | ||||||
|  |  */ | ||||||
|  | abstract class JsonTypeReference<T> : Comparable<JsonTypeReference<T>> { | ||||||
|  | 
 | ||||||
|  |     open var type: Type = Any::class.java | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 构造函数,通过反射获取泛型类型信息 | ||||||
|  |      * 仅供内部匿名子类使用 | ||||||
|  |      */ | ||||||
|  |     protected constructor() { | ||||||
|  |         val superClass: Type = this.javaClass.genericSuperclass | ||||||
|  | 
 | ||||||
|  |         // 检查是否为匿名子类,防止直接实例化导致无法获取泛型信息 | ||||||
|  |         if (superClass is Class<*>) { | ||||||
|  |             throw IllegalArgumentException( | ||||||
|  |                 "必须使用匿名子类方式创建 JsonTypeReference," + | ||||||
|  |                         "例如: new JsonTypeReference<List<String>>() {}" | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.type = (superClass as ParameterizedType).actualTypeArguments[0] | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 构造函数,直接指定类型 | ||||||
|  |      * @param type 具体的类型信息 | ||||||
|  |      */ | ||||||
|  |     protected constructor(type: Type) { | ||||||
|  |         this.type = Objects.requireNonNull(type, "Type cannot be null") | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 创建类型引用实例 | ||||||
|  |      * @param <T> 目标类型 | ||||||
|  |      * @return 类型引用实例 | ||||||
|  |      */ | ||||||
|  |     companion object { | ||||||
|  |         @JvmStatic | ||||||
|  |         fun <T> of(): JsonTypeReference<T> { | ||||||
|  |             return object : JsonTypeReference<T>() {} | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 根据 Class 创建类型引用 | ||||||
|  |          * @param clazz 目标类 | ||||||
|  |          * @param <T> 目标类型 | ||||||
|  |          * @return 类型引用实例 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun <T> of(clazz: Class<T>): JsonTypeReference<T> { | ||||||
|  |             return object : JsonTypeReference<T>(clazz) {} | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 根据 Type 创建类型引用 | ||||||
|  |          * @param type 目标类型 | ||||||
|  |          * @param <T> 目标类型 | ||||||
|  |          * @return 类型引用实例 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun <T> of(type: Type): JsonTypeReference<T> { | ||||||
|  |             return object : JsonTypeReference<T>(type) {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取原始类型(去掉泛型参数的类型) | ||||||
|  |      * @return 原始类型 Class | ||||||
|  |      */ | ||||||
|  |     @Suppress("UNCHECKED_CAST") | ||||||
|  |     fun getRawType(): Class<T> { | ||||||
|  |         var rawType: Type = type | ||||||
|  | 
 | ||||||
|  |         // 如果是参数化类型,则提取原始类型部分 | ||||||
|  |         if (type is ParameterizedType) { | ||||||
|  |             rawType = (type as ParameterizedType).rawType | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (rawType is Class<*>) { | ||||||
|  |             return rawType as Class<T> | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         throw IllegalStateException("无法获取原始类型: $type") | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun equals(other: Any?): Boolean { | ||||||
|  |         if (this === other) return true | ||||||
|  |         if (other == null || this.javaClass != other.javaClass) return false | ||||||
|  |         val that = other as JsonTypeReference<*> | ||||||
|  | 
 | ||||||
|  |         // 对于 ParameterizedType,需要更完整的比较 | ||||||
|  |         if (this.type is ParameterizedType && that.type is ParameterizedType) { | ||||||
|  |             val thisParamType = this.type as ParameterizedType | ||||||
|  |             val thatParamType = that.type as ParameterizedType | ||||||
|  | 
 | ||||||
|  |             return ( | ||||||
|  |                     Objects.equals( | ||||||
|  |                         thisParamType.rawType, | ||||||
|  |                         thatParamType.rawType | ||||||
|  |                     ) && | ||||||
|  |                             thisParamType.actualTypeArguments.contentEquals(thatParamType.actualTypeArguments) && | ||||||
|  |                             Objects.equals( | ||||||
|  |                                 thisParamType.ownerType, | ||||||
|  |                                 thatParamType.ownerType | ||||||
|  |                             ) | ||||||
|  |                     ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return Objects.equals(type, that.type) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun hashCode(): Int { | ||||||
|  |         if (type is ParameterizedType) { | ||||||
|  |             val paramType = type as ParameterizedType | ||||||
|  |             return Objects.hash( | ||||||
|  |                 paramType.rawType, | ||||||
|  |                 paramType.actualTypeArguments.contentHashCode(), | ||||||
|  |                 paramType.ownerType | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         return Objects.hash(type) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun toString(): String { | ||||||
|  |         return "JsonTypeReference{$type}" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun compareTo(other: JsonTypeReference<T>): Int { | ||||||
|  |         return this.type.toString().compareTo(other.type.toString()) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										201
									
								
								src/main/kotlin/com/mingliqiye/utils/json/JsonTypeUtils.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								src/main/kotlin/com/mingliqiye/utils/json/JsonTypeUtils.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,201 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2025 mingliqiye | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  * ProjectName mingli-utils | ||||||
|  |  * ModuleName mingli-utils.main | ||||||
|  |  * CurrentFile JsonTypeUtils.kt | ||||||
|  |  * LastUpdate 2025-09-17 11:12:06 | ||||||
|  |  * UpdateUser MingLiPro | ||||||
|  |  */ | ||||||
|  | @file:JvmName("JsonTypeUtils") | ||||||
|  | 
 | ||||||
|  | package com.mingliqiye.utils.json | ||||||
|  | 
 | ||||||
|  | import java.lang.reflect.ParameterizedType | ||||||
|  | import java.lang.reflect.Type | ||||||
|  | import java.util.* | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 检查给定的类型是否是指定类或其子类/实现类。 | ||||||
|  |  * | ||||||
|  |  * @param type          要检查的类型 | ||||||
|  |  * @param expectedClass 期望匹配的类 | ||||||
|  |  * @return 如果类型匹配则返回 true,否则返回 false | ||||||
|  |  */ | ||||||
|  | fun isTypeOf(type: Type, expectedClass: Class<*>): Boolean { | ||||||
|  |     return when (type) { | ||||||
|  |         is Class<*> -> expectedClass.isAssignableFrom(type) | ||||||
|  |         is ParameterizedType -> isTypeOf(type.rawType, expectedClass) | ||||||
|  |         else -> false | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取泛型类型的参数类型。 | ||||||
|  |  * | ||||||
|  |  * @param type  泛型类型 | ||||||
|  |  * @param index 参数索引(从0开始) | ||||||
|  |  * @return 指定位置的泛型参数类型 | ||||||
|  |  * @throws IllegalArgumentException 当无法获取指定索引的泛型参数时抛出异常 | ||||||
|  |  */ | ||||||
|  | fun getGenericParameter(type: Type, index: Int): Type { | ||||||
|  |     if (type is ParameterizedType) { | ||||||
|  |         val typeArgs = type.actualTypeArguments | ||||||
|  |         if (index >= 0 && index < typeArgs.size) { | ||||||
|  |             return typeArgs[index] | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     throw IllegalArgumentException( | ||||||
|  |         "无法获取泛型参数: $type at index $index" | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取类型名称,支持普通类和泛型类型。 | ||||||
|  |  * | ||||||
|  |  * @param type 类型对象 | ||||||
|  |  * @return 类型名称字符串 | ||||||
|  |  */ | ||||||
|  | fun getTypeName(type: Type): String { | ||||||
|  |     return when (type) { | ||||||
|  |         is Class<*> -> type.simpleName | ||||||
|  |         is ParameterizedType -> { | ||||||
|  |             val rawType = type.rawType as Class<*> | ||||||
|  |             val typeArgs = type.actualTypeArguments | ||||||
|  | 
 | ||||||
|  |             val sb = StringBuilder(rawType.simpleName) | ||||||
|  |             sb.append("<") | ||||||
|  |             for (i in typeArgs.indices) { | ||||||
|  |                 if (i > 0) sb.append(", ") | ||||||
|  |                 sb.append(getTypeName(typeArgs[i])) | ||||||
|  |             } | ||||||
|  |             sb.append(">") | ||||||
|  |             sb.toString() | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else -> type.typeName | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 创建一个表示数组类型的引用对象。 | ||||||
|  |  * | ||||||
|  |  * @param componentType 数组元素的类型 | ||||||
|  |  * @param <T>           元素类型 | ||||||
|  |  * @return 表示数组类型的 JsonTypeReference 对象 | ||||||
|  |  */ | ||||||
|  | fun <T> arrayType(componentType: Class<T>): JsonTypeReference<Array<T>> { | ||||||
|  |     return object : JsonTypeReference<Array<T>>() { | ||||||
|  |         private val arrayType: Type = java.lang.reflect.Array.newInstance( | ||||||
|  |             componentType, 0 | ||||||
|  |         ).javaClass | ||||||
|  | 
 | ||||||
|  |         override var type: Type = Any::class.java | ||||||
|  |             get() = object : ParameterizedType { | ||||||
|  |                 private val actualTypeArguments = arrayOf<Type>(componentType) | ||||||
|  | 
 | ||||||
|  |                 override fun getActualTypeArguments(): Array<Type> { | ||||||
|  |                     return actualTypeArguments | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun getRawType(): Type { | ||||||
|  |                     return arrayType | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun getOwnerType(): Type? { | ||||||
|  |                     return null | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 创建一个表示 List 类型的引用对象。 | ||||||
|  |  * | ||||||
|  |  * @param componentType List 中元素的类型 | ||||||
|  |  * @param <T>           元素类型 | ||||||
|  |  * @return 表示 List 类型的 JsonTypeReference 对象 | ||||||
|  |  * @throws IllegalArgumentException 如果 componentType 为 null,则抛出异常 | ||||||
|  |  */ | ||||||
|  | fun <T> listType(componentType: Class<T>): JsonTypeReference<List<T>> { | ||||||
|  | 
 | ||||||
|  |     return object : JsonTypeReference<List<T>>() { | ||||||
|  |         override var type: Type = Any::class.java | ||||||
|  |             get() = object : ParameterizedType { | ||||||
|  |                 override fun getActualTypeArguments(): Array<Type> { | ||||||
|  |                     return arrayOf(componentType) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun getRawType(): Type { | ||||||
|  |                     return List::class.java | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun getOwnerType(): Type? { | ||||||
|  |                     return null | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 创建一个表示 Map 类型的引用对象。 | ||||||
|  |  * | ||||||
|  |  * @param keyType   Map 键的类型 | ||||||
|  |  * @param valueType Map 值的类型 | ||||||
|  |  * @param <K>       键类型 | ||||||
|  |  * @param <V>       值类型 | ||||||
|  |  * @return 表示 Map 类型的 JsonTypeReference 对象 | ||||||
|  |  * @throws IllegalArgumentException 如果 keyType 或 valueType 为 null,则抛出异常 | ||||||
|  |  */ | ||||||
|  | fun <K, V> MapType(keyType: Class<K>, valueType: Class<V>): JsonTypeReference<Map<K, V>> { | ||||||
|  | 
 | ||||||
|  |     return object : JsonTypeReference<Map<K, V>>() { | ||||||
|  |         override var type: Type = Any::class.java | ||||||
|  |             get() = object : ParameterizedType { | ||||||
|  |                 override fun getActualTypeArguments(): Array<Type> { | ||||||
|  |                     return arrayOf(keyType, valueType) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun getRawType(): Type { | ||||||
|  |                     return Map::class.java | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun getOwnerType(): Type? { | ||||||
|  |                     return null | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun equals(other: Any?): Boolean { | ||||||
|  |                     if (this === other) return true | ||||||
|  |                     if (other !is ParameterizedType) return false | ||||||
|  | 
 | ||||||
|  |                     val that = other | ||||||
|  |                     return (Objects.equals( | ||||||
|  |                         rawType, | ||||||
|  |                         that.rawType | ||||||
|  |                     ) && actualTypeArguments.contentEquals(that.actualTypeArguments) && Objects.equals( | ||||||
|  |                         ownerType, | ||||||
|  |                         that.ownerType | ||||||
|  |                     )) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun hashCode(): Int { | ||||||
|  |                     return (actualTypeArguments.contentHashCode() xor Objects.hashCode(rawType) xor Objects.hashCode( | ||||||
|  |                         ownerType | ||||||
|  |                     )) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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 |  | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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-14 18:19:29 |  * 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() } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,18 +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 09:53:43 |  * LastUpdate 2025-09-20 13:22:11 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| package com.mingliqiye.utils | @file:JvmName("Main") | ||||||
|  | 
 | ||||||
|  | package com.mingliqiye.utils.main | ||||||
| 
 | 
 | ||||||
| import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration | import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration | ||||||
| import com.mingliqiye.utils.time.DateTime | import com.mingliqiye.utils.stream.SuperStream | ||||||
| 
 | 
 | ||||||
| fun main() { | fun main() { | ||||||
|     AutoConfiguration.printBanner() |     AutoConfiguration.printBanner() | ||||||
|     println(DateTime.now()) |     val data = SuperStream.of(Array(0) { 1 }) | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| fun test() { | 
 | ||||||
|  |     println(data) | ||||||
| } | } | ||||||
							
								
								
									
										64
									
								
								src/main/kotlin/com/mingliqiye/utils/metadata/MetaData.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/main/kotlin/com/mingliqiye/utils/metadata/MetaData.kt
									
									
									
									
									
										Normal 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"] ?: "" | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -15,35 +15,28 @@ | |||||||
|  * |  * | ||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile NetworkException.java |  * CurrentFile QueryWrapper.kt | ||||||
|  * LastUpdate 2025-09-09 08:37:33 |  * LastUpdate 2025-09-20 14:21:44 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.mingliqiye.utils.network; | package com.mingliqiye.utils.mybatisplus | ||||||
|  | 
 | ||||||
|  | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper | ||||||
|  | import com.baomidou.mybatisplus.core.mapper.BaseMapper | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 网络异常类,用于处理网络相关的运行时异常 |  * BaseMapperQuery接口扩展了BaseMapper,提供了通用的查询包装器功能 | ||||||
|  * |  * | ||||||
|  * @author MingLiPro |  * @param T 实体类类型 | ||||||
|  */ |  */ | ||||||
| public class NetworkException extends RuntimeException { | interface BaseMapperQuery<T> : BaseMapper<T> { | ||||||
| 
 |     /** | ||||||
| 	/** |      * 创建并返回一个新的QueryWrapper实例 | ||||||
| 	 * 构造一个带有指定详细消息的网络异常 |      * | ||||||
| 	 * |      * @return QueryWrapper<T> 返回类型化的查询包装器实例 | ||||||
| 	 * @param message 异常的详细消息 |      */ | ||||||
| 	 */ |     fun queryWrapper(): QueryWrapper<T> { | ||||||
| 	public NetworkException(String message) { |         return QueryWrapper<T>() | ||||||
| 		super(message); |     } | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 构造一个网络异常,指定原因异常 |  | ||||||
| 	 * |  | ||||||
| 	 * @param e 导致此异常的原因异常 |  | ||||||
| 	 */ |  | ||||||
| 	public NetworkException(Exception e) { |  | ||||||
| 		super(e); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
							
								
								
									
										350
									
								
								src/main/kotlin/com/mingliqiye/utils/network/AddressPort.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								src/main/kotlin/com/mingliqiye/utils/network/AddressPort.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,350 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2025 mingliqiye | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  * ProjectName mingli-utils | ||||||
|  |  * ModuleName mingli-utils.main | ||||||
|  |  * CurrentFile AddressPort.kt | ||||||
|  |  * LastUpdate 2025-09-15 22:01:27 | ||||||
|  |  * UpdateUser MingLiPro | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.mingliqiye.utils.network | ||||||
|  | 
 | ||||||
|  | import java.io.Serializable | ||||||
|  | import java.net.InetAddress | ||||||
|  | import java.net.InetSocketAddress | ||||||
|  | import java.net.UnknownHostException | ||||||
|  | import java.util.regex.Pattern | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 网络地址类,用于表示一个网络地址(IP或域名),并提供相关操作。 | ||||||
|  |  * 支持IPv4和IPv6地址的解析与验证。 | ||||||
|  |  * | ||||||
|  |  * @author MingLiPro | ||||||
|  |  */ | ||||||
|  | class NetworkAddress private constructor(domip: String) : Serializable { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * IPv6标识 | ||||||
|  |      */ | ||||||
|  |     companion object { | ||||||
|  |         const val IPV6 = 6 | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * IPv4标识 | ||||||
|  |          */ | ||||||
|  |         const val IPV4 = 4 | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * IPv4地址正则表达式 | ||||||
|  |          */ | ||||||
|  |         private const val IPV4REG = | ||||||
|  |             "^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2" + "(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$" | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 编译后的IPv4地址匹配模式 | ||||||
|  |          */ | ||||||
|  |         private val IPV4_PATTERN = Pattern.compile(IPV4REG) | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * IPv6地址正则表达式 | ||||||
|  |          */ | ||||||
|  |         private const val IPV6REG = | ||||||
|  |             "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" + "^(::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$" + "|" + "^(::)$|" + "^([0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})$|" + "^(([0-9a-fA-F]{1,4}:){6}(([0-9]{1,3}\\.){3}[0-9]{1,3}))$|" + "^::([fF]{4}:)?(([0-9]{1,3}\\.){3}[0-9]{1,3})$" | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 编译后的IPv6地址匹配模式 | ||||||
|  |          */ | ||||||
|  |         private val IPV6_PATTERN = Pattern.compile(IPV6REG) | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 静态工厂方法,创建 NetworkAddress 实例。 | ||||||
|  |          * | ||||||
|  |          * @param domip 可能是IP地址或域名的字符串 | ||||||
|  |          * @return 新建的 NetworkAddress 实例 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun of(domip: String): NetworkAddress { | ||||||
|  |             return NetworkAddress(domip) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 静态工厂方法,通过 InetAddress 创建 NetworkAddress 实例。 | ||||||
|  |          * | ||||||
|  |          * @param inetAddress InetAddress 对象 | ||||||
|  |          * @return 新建的 NetworkAddress 实例 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun of(inetAddress: InetAddress): NetworkAddress { | ||||||
|  |             return NetworkAddress(inetAddress.hostAddress) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 从DNS服务器解析域名获取对应的IP地址。 | ||||||
|  |          * | ||||||
|  |          * @param domain 域名 | ||||||
|  |          * @return 解析出的第一个IP地址 | ||||||
|  |          * @throws UnknownHostException 如果域名无法解析 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         @Throws(UnknownHostException::class) | ||||||
|  |         fun getHostIp(domain: String): String { | ||||||
|  |             val addresses = InetAddress.getAllByName(domain.trim()) | ||||||
|  |             return addresses[0].hostAddress | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 检测给定字符串是否为有效的IPv4或IPv6地址。 | ||||||
|  |          * | ||||||
|  |          * @param ip 要检测的IP地址字符串 | ||||||
|  |          * @return 4 表示IPv4,6 表示IPv6 | ||||||
|  |          * @throws NetworkException 如果IP格式无效 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun testIp(ip: String?): Int { | ||||||
|  |             if (ip == null) { | ||||||
|  |                 throw NetworkException("IP地址不能为null") | ||||||
|  |             } | ||||||
|  |             val trimmedIp = ip.trim() | ||||||
|  | 
 | ||||||
|  |             // 判断是否匹配IPv4格式 | ||||||
|  |             if (IPV4_PATTERN.matcher(trimmedIp).matches()) { | ||||||
|  |                 return IPV4 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // 判断是否匹配IPv6格式 | ||||||
|  |             if (IPV6_PATTERN.matcher(trimmedIp).matches()) { | ||||||
|  |                 return IPV6 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // 不符合任一格式时抛出异常 | ||||||
|  |             throw NetworkException( | ||||||
|  |                 "[$ip] 不是有效的IPv4或IPv6地址" | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * IP地址类型:4 表示 IPv4,6 表示 IPv6 | ||||||
|  |      */ | ||||||
|  |     var iPv: Int = 0 | ||||||
|  |         private set | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * IP地址字符串 | ||||||
|  |      */ | ||||||
|  |     var ip: String? = null | ||||||
|  |         private set | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 域名(如果输入的是域名) | ||||||
|  |      */ | ||||||
|  |     private var domain: String? = null | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 标识是否是域名解析来的IP | ||||||
|  |      */ | ||||||
|  |     private var isdom = false | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 构造方法,根据传入的字符串判断是IP地址还是域名,并进行相应处理。 | ||||||
|  |      * | ||||||
|  |      * @param domip 可能是IP地址或域名的字符串 | ||||||
|  |      */ | ||||||
|  |     init { | ||||||
|  |         try { | ||||||
|  |             // 尝试将输入识别为IP地址 | ||||||
|  |             this.iPv = testIp(domip) | ||||||
|  |             this.ip = domip | ||||||
|  |         } catch (e: NetworkException) { | ||||||
|  |             try { | ||||||
|  |                 // 如果不是有效IP,则尝试作为域名解析 | ||||||
|  |                 val ips = getHostIp(domip) | ||||||
|  |                 this.iPv = testIp(ips) | ||||||
|  |                 this.ip = ips | ||||||
|  |                 this.isdom = true | ||||||
|  |                 this.domain = domip | ||||||
|  |             } catch (ex: UnknownHostException) { | ||||||
|  |                 throw NetworkException(ex) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将当前 NetworkAddress 转换为 InetAddress 对象。 | ||||||
|  |      * | ||||||
|  |      * @return InetAddress 对象 | ||||||
|  |      */ | ||||||
|  |     fun toInetAddress(): InetAddress { | ||||||
|  |         try { | ||||||
|  |             return InetAddress.getByName(if (ip != null) ip else domain) | ||||||
|  |         } catch (e: UnknownHostException) { | ||||||
|  |             throw RuntimeException(e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 返回 NetworkAddress 的字符串表示形式。 | ||||||
|  |      * | ||||||
|  |      * @return 字符串表示 | ||||||
|  |      */ | ||||||
|  |     override fun toString(): String { | ||||||
|  |         return if (isdom) "NetworkAddress(IP='$ip',type='$iPv',domain='$domain')" | ||||||
|  |         else "NetworkAddress(IP='$ip',type='$iPv')" | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class NetworkPort : Serializable { | ||||||
|  |     val port: Int | ||||||
|  | 
 | ||||||
|  |     constructor(port: Int) { | ||||||
|  |         testPort(port) | ||||||
|  |         this.port = port | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     companion object { | ||||||
|  |         fun testPort(port: Int) { | ||||||
|  |             // 验证端口号范围是否在0-65535之间 | ||||||
|  |             if (port !in 0..65535) { | ||||||
|  |                 throw NetworkException("$port 不是正确的端口号") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class NetworkException : RuntimeException { | ||||||
|  |     /** | ||||||
|  |      * 构造一个带有指定详细消息的网络异常 | ||||||
|  |      * | ||||||
|  |      * @param message 异常的详细消息 | ||||||
|  |      */ | ||||||
|  |     constructor(message: String?) : super(message) | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 构造一个网络异常,指定原因异常 | ||||||
|  |      * | ||||||
|  |      * @param e 导致此异常的原因异常 | ||||||
|  |      */ | ||||||
|  |     constructor(e: Exception?) : super(e) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * IP和端口聚集类,用于封装网络地址与端口信息。 | ||||||
|  |  * 该类提供了与InetSocketAddress之间的相互转换功能。 | ||||||
|  |  * | ||||||
|  |  * @author MingLiPro | ||||||
|  |  * @see java.net.InetSocketAddress | ||||||
|  |  */ | ||||||
|  | class NetworkEndpoint private constructor( | ||||||
|  |     val networkAddress: NetworkAddress, val networkPort: NetworkPort | ||||||
|  | ) : Serializable { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     companion object { | ||||||
|  |         /** | ||||||
|  |          * 根据给定的InetSocketAddress对象创建NetworkEndpoint实例。 | ||||||
|  |          * | ||||||
|  |          * @param address InetSocketAddress对象 | ||||||
|  |          * @return 新建的NetworkEndpoint实例 | ||||||
|  |          * @see java.net.InetSocketAddress | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun of(address: InetSocketAddress): NetworkEndpoint { | ||||||
|  |             return NetworkEndpoint( | ||||||
|  |                 NetworkAddress.of(address.hostString), NetworkPort(address.port) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 根据主机名或IP字符串和端口号创建NetworkEndpoint实例。 | ||||||
|  |          * | ||||||
|  |          * @param s 主机名或IP地址字符串 | ||||||
|  |          * @param i 端口号 | ||||||
|  |          * @return 新建的NetworkEndpoint实例 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun of(s: String, i: Int): NetworkEndpoint { | ||||||
|  |             val networkAddress = NetworkAddress.of(s) | ||||||
|  |             val networkPort = NetworkPort(i) | ||||||
|  |             return NetworkEndpoint(networkAddress, networkPort) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * 根据"host:port"格式的字符串创建NetworkEndpoint实例。 | ||||||
|  |          * 例如:"127.0.0.1:8080" | ||||||
|  |          * | ||||||
|  |          * @param s "host:port"格式的字符串 | ||||||
|  |          * @return 新建的NetworkEndpoint实例 | ||||||
|  |          */ | ||||||
|  |         @JvmStatic | ||||||
|  |         fun of(s: String): NetworkEndpoint { | ||||||
|  |             val lastColonIndex = s.lastIndexOf(':') | ||||||
|  |             return of( | ||||||
|  |                 s.take(lastColonIndex), s.substring(lastColonIndex + 1).toInt() | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将当前NetworkEndpoint转换为InetSocketAddress对象。 | ||||||
|  |      * | ||||||
|  |      * @return 对应的InetSocketAddress对象 | ||||||
|  |      * @see InetSocketAddress | ||||||
|  |      */ | ||||||
|  |     fun toInetSocketAddress(): InetSocketAddress { | ||||||
|  |         return InetSocketAddress( | ||||||
|  |             networkAddress.toInetAddress(), networkPort.port | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 将当前NetworkEndpoint转换为"host:port"格式的字符串。 | ||||||
|  |      * 例如:"127.0.0.1:25563" | ||||||
|  |      * | ||||||
|  |      * @return 格式化后的字符串 | ||||||
|  |      */ | ||||||
|  |     fun toHostPortString(): String { | ||||||
|  |         return "${networkAddress.ip}:${networkPort.port}" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 返回NetworkEndpoint的详细字符串表示形式。 | ||||||
|  |      * 格式:NetworkEndpoint(IP=...,Port=...,Endpoint=...) | ||||||
|  |      * | ||||||
|  |      * @return 包含详细信息的字符串 | ||||||
|  |      */ | ||||||
|  |     override fun toString(): String { | ||||||
|  |         return "NetworkEndpoint(IP=${networkAddress.ip},Port=${networkPort.port},Endpoint=${toHostPortString()})" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取主机名或IP地址字符串。 | ||||||
|  |      * | ||||||
|  |      * @return 主机名或IP地址 | ||||||
|  |      */ | ||||||
|  |     fun host(): String { | ||||||
|  |         return networkAddress.ip ?: "" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 获取端口号。 | ||||||
|  |      * | ||||||
|  |      * @return 端口号 | ||||||
|  |      */ | ||||||
|  |     fun port(): Int { | ||||||
|  |         return networkPort.port | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										100
									
								
								src/main/kotlin/com/mingliqiye/utils/number/NumberUtils.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/main/kotlin/com/mingliqiye/utils/number/NumberUtils.kt
									
									
									
									
									
										Normal 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) | ||||||
|  | } | ||||||
| @ -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() |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,13 +16,15 @@ | |||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile RandomBytes.kt |  * CurrentFile RandomBytes.kt | ||||||
|  * LastUpdate 2025-09-15 09:54:33 |  * LastUpdate 2025-09-16 17:42:26 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| @file:JvmName("RandomBytes") | @file:JvmName("RandomBytes") | ||||||
| 
 | 
 | ||||||
| package com.mingliqiye.utils.random | package com.mingliqiye.utils.random | ||||||
| 
 | 
 | ||||||
|  | import java.security.SecureRandom | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * 生成指定长度的随机字节数组 |  * 生成指定长度的随机字节数组 | ||||||
|  * @param length 数组长度 |  * @param length 数组长度 | ||||||
| @ -78,3 +80,14 @@ fun randomByteNoHave(from: Byte, to: Byte): Byte { | |||||||
|     val randomValue = randomIntNoHave(fromInt, toInt) |     val randomValue = randomIntNoHave(fromInt, toInt) | ||||||
|     return (randomValue and 0xFF).toByte() |     return (randomValue and 0xFF).toByte() | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | val secureRandom: SecureRandom by lazy { | ||||||
|  |     SecureRandom.getInstanceStrong() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | fun randomByteSecure(size: Int): ByteArray { | ||||||
|  |     val bytes = ByteArray(size) | ||||||
|  |     secureRandom.nextBytes(bytes) | ||||||
|  |     return bytes | ||||||
|  | } | ||||||
|  | |||||||
| @ -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 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,6 +1,29 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2025 mingliqiye | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  * ProjectName mingli-utils | ||||||
|  |  * ModuleName mingli-utils.main | ||||||
|  |  * CurrentFile AesUtils.kt | ||||||
|  |  * LastUpdate 2025-09-19 20:18:09 | ||||||
|  |  * UpdateUser MingLiPro | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| @file:JvmName("AesUtils") | @file:JvmName("AesUtils") | ||||||
| 
 | 
 | ||||||
| package com.mingliqiye.utils.security | package com.mingliqiye.utils.security | ||||||
|  | 
 | ||||||
| import javax.crypto.Cipher | import javax.crypto.Cipher | ||||||
| import javax.crypto.spec.GCMParameterSpec | import javax.crypto.spec.GCMParameterSpec | ||||||
| 
 | 
 | ||||||
| @ -9,8 +32,8 @@ const val AES_GCM_NO_PADDING = "AES/GCM/NoPadding" | |||||||
| const val AES_GCM_NO_PADDING_IV_LENGTH = 12 | const val AES_GCM_NO_PADDING_IV_LENGTH = 12 | ||||||
| const val AES_GCM_NO_PADDING_TAG_LENGTH = 16 | const val AES_GCM_NO_PADDING_TAG_LENGTH = 16 | ||||||
| 
 | 
 | ||||||
| fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray,iv: ByteArray): ByteArray { | fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray, iv: ByteArray): ByteArray { | ||||||
|     val secretKeySpec = createSecretKeySpec(ALGORITHM,key) |     val secretKeySpec = createSecretKeySpec(ALGORITHM, key) | ||||||
|     val cipher = Cipher.getInstance(AES_GCM_NO_PADDING) |     val cipher = Cipher.getInstance(AES_GCM_NO_PADDING) | ||||||
|     val gcmParameterSpec = GCMParameterSpec( |     val gcmParameterSpec = GCMParameterSpec( | ||||||
|         AES_GCM_NO_PADDING_TAG_LENGTH * 8, |         AES_GCM_NO_PADDING_TAG_LENGTH * 8, | ||||||
| @ -19,17 +42,13 @@ fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray,iv: ByteArray): ByteAr | |||||||
|     cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec) |     cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec) | ||||||
|     return cipher.doFinal(src) |     return cipher.doFinal(src) | ||||||
| } | } | ||||||
| fun encryptAesGcmNoPadding(src: ByteArray, key: String,iv: ByteArray): ByteArray { | 
 | ||||||
|  | fun encryptAesGcmNoPadding(src: ByteArray, key: String, iv: ByteArray): ByteArray { | ||||||
|     return encryptAesGcmNoPadding(src, key.toByteArray(), iv) |     return encryptAesGcmNoPadding(src, key.toByteArray(), iv) | ||||||
| } | } | ||||||
| fun encryptAesGcmNoPadding(src: String, key: String,iv: ByteArray): ByteArray { | 
 | ||||||
|  | fun encryptAesGcmNoPadding(src: String, key: String, iv: ByteArray): ByteArray { | ||||||
|     return encryptAesGcmNoPadding(src.toByteArray(), key.toByteArray(), iv) |     return encryptAesGcmNoPadding(src.toByteArray(), key.toByteArray(), iv) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun main() { |  | ||||||
|     val iv = getRandomBytes(16) |  | ||||||
|     println(encryptAesGcmNoPadding("mingliqiye","key", iv)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,25 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2025 mingliqiye | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  * | ||||||
|  |  * ProjectName mingli-utils | ||||||
|  |  * ModuleName mingli-utils.main | ||||||
|  |  * CurrentFile SecureUtils.kt | ||||||
|  |  * LastUpdate 2025-09-15 22:32:50 | ||||||
|  |  * UpdateUser MingLiPro | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| @file:JvmName("SecureUtils") | @file:JvmName("SecureUtils") | ||||||
| 
 | 
 | ||||||
| package com.mingliqiye.utils.security | package com.mingliqiye.utils.security | ||||||
| @ -25,9 +47,9 @@ fun createSecretKey(algorithm: String, data: String): ByteArray { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun createSecretKeySpec(algorithm: String, data: String): SecretKey { | fun createSecretKeySpec(algorithm: String, data: String): SecretKey { | ||||||
|     return SecretKeySpec( createSecretKey(algorithm,data), algorithm) |     return SecretKeySpec(createSecretKey(algorithm, data), algorithm) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun createSecretKeySpec(algorithm: String, data: ByteArray): SecretKey { | fun createSecretKeySpec(algorithm: String, data: ByteArray): SecretKey { | ||||||
|     return SecretKeySpec( createSecretKey(algorithm,data), algorithm) |     return SecretKeySpec(createSecretKey(algorithm, data), algorithm) | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,14 +16,17 @@ | |||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile AutoConfiguration.kt |  * CurrentFile AutoConfiguration.kt | ||||||
|  * LastUpdate 2025-09-15 08:51:52 |  * LastUpdate 2025-09-20 10:47:00 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.mingliqiye.utils.springboot.autoconfigure | package com.mingliqiye.utils.springboot.autoconfigure | ||||||
| 
 | 
 | ||||||
| import com.mingliqiye.utils.logger.mingLiLoggerFactory | import com.mingliqiye.utils.logger.mingLiLoggerFactory | ||||||
| import com.mingliqiye.utils.system.getJdkVersion | import com.mingliqiye.utils.system.computerName | ||||||
|  | import com.mingliqiye.utils.system.getPid | ||||||
|  | import com.mingliqiye.utils.system.jdkVersion | ||||||
|  | import com.mingliqiye.utils.system.userName | ||||||
| import com.mingliqiye.utils.time.DateTime | import com.mingliqiye.utils.time.DateTime | ||||||
| import com.mingliqiye.utils.time.Formatter | import com.mingliqiye.utils.time.Formatter | ||||||
| import org.springframework.context.annotation.ComponentScan | import org.springframework.context.annotation.ComponentScan | ||||||
| @ -31,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 { | ||||||
| @ -51,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) | ||||||
| @ -63,8 +63,11 @@ open class AutoConfiguration { | |||||||
|                         metaData.append(String(buffer, 0, readlen)) |                         metaData.append(String(buffer, 0, readlen)) | ||||||
|                     } |                     } | ||||||
|                     val da = metaData.toString().split("\n").toMutableList() |                     val da = metaData.toString().split("\n").toMutableList() | ||||||
|                     da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7)) |                     da.add("jdkRuntime=$jdkVersion") | ||||||
|                     da.add("jdkRuntime=" + getJdkVersion()) |                     da.add("pid=$getPid") | ||||||
|  |                     da.add("computerName=$computerName") | ||||||
|  |                     da.add("userName=$userName") | ||||||
|  |                     da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)) | ||||||
|                     da.forEach { s: String -> |                     da.forEach { s: String -> | ||||||
|                         val d = s.trim { it <= ' ' }.split("=".toRegex(), 2).toTypedArray() |                         val d = s.trim { it <= ' ' }.split("=".toRegex(), 2).toTypedArray() | ||||||
|                         if (d.size >= 2) { |                         if (d.size >= 2) { | ||||||
|  | |||||||
| @ -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-14 22:10:45 |  * 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 | ||||||
| @ -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>() |  | ||||||
| } |  | ||||||
| @ -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-14 21:46:14 |  * 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() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,26 +16,34 @@ | |||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile SystemUtil.kt |  * CurrentFile SystemUtil.kt | ||||||
|  * LastUpdate 2025-09-15 11:18:34 |  * 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.net.Inet4Address | import java.net.Inet4Address | ||||||
|  | import java.net.InetAddress | ||||||
| import java.net.NetworkInterface | import java.net.NetworkInterface | ||||||
| import java.net.SocketException | import java.net.SocketException | ||||||
| 
 | 
 | ||||||
| private val osName: String? = System.getProperties().getProperty("os.name") | /** | ||||||
|  |  * 操作系统名称属性,延迟初始化 | ||||||
|  |  */ | ||||||
|  | val osName: String? by lazy { | ||||||
|  |     System.getProperties().getProperty("os.name") | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 判断当前操作系统是否为Windows系统 |  * 判断当前操作系统是否为Windows系统 | ||||||
|  * |  * | ||||||
|  * @return 如果是Windows系统返回true,否则返回false |  * @return 如果是Windows系统返回true,否则返回false | ||||||
|  */ |  */ | ||||||
| fun isWindows(): Boolean { | val isWindows: Boolean by lazy { | ||||||
|     return osName != null && osName.startsWith("Windows") |     osName != null && osName!!.startsWith("Windows") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -43,8 +51,8 @@ fun isWindows(): Boolean { | |||||||
|  * |  * | ||||||
|  * @return 如果是Mac系统返回true,否则返回false |  * @return 如果是Mac系统返回true,否则返回false | ||||||
|  */ |  */ | ||||||
| fun isMac(): Boolean { | val isMac: Boolean by lazy { | ||||||
|     return osName != null && osName.startsWith("Mac") |     osName != null && osName!!.startsWith("Mac") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -52,31 +60,30 @@ fun isMac(): Boolean { | |||||||
|  * |  * | ||||||
|  * @return 如果是Unix/Linux系统返回true,否则返回false |  * @return 如果是Unix/Linux系统返回true,否则返回false | ||||||
|  */ |  */ | ||||||
| fun isUnix(): Boolean { | val isUnix: Boolean by lazy { | ||||||
|     if (osName == null) { |     if (osName == null) { | ||||||
|         return false |         false | ||||||
|  |     } else { | ||||||
|  |         (osName!!.startsWith("Linux") || osName!!.startsWith("AIX") || osName!!.startsWith("SunOS") || osName!!.startsWith( | ||||||
|  |             "Mac OS X" | ||||||
|  |         ) || osName!!.startsWith( | ||||||
|  |             "FreeBSD" | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
|     return (osName.startsWith("Linux") || osName.startsWith("AIX") || osName.startsWith("SunOS") || osName.startsWith("Mac OS X") || osName.startsWith( |  | ||||||
|         "FreeBSD" |  | ||||||
|     )) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 获取JDK版本号 |  * JDK版本号属性,延迟初始化 | ||||||
|  * |  | ||||||
|  * @return JDK版本号字符串 |  | ||||||
|  */ |  */ | ||||||
| fun getJdkVersion(): String? { | val jdkVersion: String? by lazy { | ||||||
|     return System.getProperty("java.specification.version") |     System.getProperty("java.specification.version") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 获取Java版本号的整数形式 |  * Java版本号的整数形式属性,延迟初始化 | ||||||
|  * |  | ||||||
|  * @return Java版本号的整数形式(如:8、11、17等) |  | ||||||
|  */ |  */ | ||||||
| fun getJavaVersionAsInteger(): Int { | val javaVersionAsInteger: Int by lazy { | ||||||
|     val version = getJdkVersion() |     val version = jdkVersion | ||||||
|     if (version == null || version.isEmpty()) { |     if (version == null || version.isEmpty()) { | ||||||
|         throw IllegalStateException( |         throw IllegalStateException( | ||||||
|             "Unable to determine Java version from property 'java.specification.version'" |             "Unable to determine Java version from property 'java.specification.version'" | ||||||
| @ -98,7 +105,7 @@ fun getJavaVersionAsInteger(): Int { | |||||||
|         } |         } | ||||||
|         version.take(2) |         version.take(2) | ||||||
|     } |     } | ||||||
|     return uversion.toInt() |     uversion.toInt() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -106,18 +113,17 @@ fun getJavaVersionAsInteger(): Int { | |||||||
|  * |  * | ||||||
|  * @return 如果JDK版本大于8返回true,否则返回false |  * @return 如果JDK版本大于8返回true,否则返回false | ||||||
|  */ |  */ | ||||||
| fun isJdk8Plus(): Boolean { | val isJdk8Plus: Boolean by lazy { | ||||||
|     return getJavaVersionAsInteger() > 8 |     javaVersionAsInteger > 8 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 获取本地IP地址数组 |  * 本地IP地址数组,延迟初始化 | ||||||
|  * |  * | ||||||
|  * @return 本地IP地址字符串数组 |  | ||||||
|  * @throws RuntimeException 当获取网络接口信息失败时抛出 |  * @throws RuntimeException 当获取网络接口信息失败时抛出 | ||||||
|  */ |  */ | ||||||
| fun getLocalIps(): Array<String> { | val localIps: Array<String> by lazy { | ||||||
|     return try { |     try { | ||||||
|         val ipList: MutableList<String> = ArrayList() |         val ipList: MutableList<String> = ArrayList() | ||||||
|         val interfaces = NetworkInterface.getNetworkInterfaces() |         val interfaces = NetworkInterface.getNetworkInterfaces() | ||||||
| 
 | 
 | ||||||
| @ -145,22 +151,18 @@ fun getLocalIps(): Array<String> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 获取本地IP地址列表 |  * 本地IP地址列表,延迟初始化 | ||||||
|  * |  | ||||||
|  * @return 本地IP地址的字符串列表 |  | ||||||
|  */ |  */ | ||||||
| fun getLocalIpsByList(): List<String> { | val localIpsByList: List<String> by lazy { | ||||||
|     return getLocalIps().toList() |     localIps.toList() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 获取本地回环地址 |  * 本地回环地址数组,延迟初始化 | ||||||
|  * |  | ||||||
|  * @return 回环地址字符串,通常为"127.0.0.1" |  | ||||||
|  */ |  */ | ||||||
| fun getLoopbackIps(): Array<String> { | val loopbackIps: Array<String> by lazy { | ||||||
|     val strings: MutableList<String> = ArrayList(3) |     val strings: MutableList<String> = ArrayList(3) | ||||||
|     return try { |     return@lazy try { | ||||||
|         val interfaces = NetworkInterface.getNetworkInterfaces() |         val interfaces = NetworkInterface.getNetworkInterfaces() | ||||||
| 
 | 
 | ||||||
|         while (interfaces.hasMoreElements()) { |         while (interfaces.hasMoreElements()) { | ||||||
| @ -183,11 +185,193 @@ fun getLoopbackIps(): Array<String> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 获取本地回环地址IP列表 |  * 本地回环地址IP列表,延迟初始化 | ||||||
|  * |  | ||||||
|  * @return 本地回环地址IP字符串列表的副本 |  | ||||||
|  */ |  */ | ||||||
| fun getLoopbackIpsByList(): List<String> { | val loopbackIpsByList: List<String> by lazy { | ||||||
|     // 将本地回环地址IP数组转换为列表并返回 |     loopbackIps.toList() | ||||||
|     return getLoopbackIps().toList() | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取当前进程的PID | ||||||
|  |  * | ||||||
|  |  * @return 进程ID,如果无法获取则返回-1 | ||||||
|  |  */ | ||||||
|  | val getPid: Long by lazy { | ||||||
|  |     try { | ||||||
|  |         val name = ManagementFactory.getRuntimeMXBean().name | ||||||
|  |         val index = name.indexOf('@') | ||||||
|  |         if (index > 0) { | ||||||
|  |             name.take(index).toLong() | ||||||
|  |         } else { | ||||||
|  |             -1L | ||||||
|  |         } | ||||||
|  |     } catch (e: Exception) { | ||||||
|  |         -1L | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取当前进程的PID字符串形式 | ||||||
|  |  * | ||||||
|  |  * @return 进程ID字符串,如果无法获取则返回"-1" | ||||||
|  |  */ | ||||||
|  | val pidAsString: String by lazy { | ||||||
|  |     try { | ||||||
|  |         val name = ManagementFactory.getRuntimeMXBean().name | ||||||
|  |         val index = name.indexOf('@') | ||||||
|  |         if (index > 0) { | ||||||
|  |             name.take(index) | ||||||
|  |         } else { | ||||||
|  |             "-1" | ||||||
|  |         } | ||||||
|  |     } catch (e: Exception) { | ||||||
|  |         "-1" | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取计算机名 | ||||||
|  |  * | ||||||
|  |  * @return 计算机名,如果无法获取则返回"unknown" | ||||||
|  |  */ | ||||||
|  | val computerName: String by lazy { | ||||||
|  |     try { | ||||||
|  |         var name = System.getenv("COMPUTERNAME") | ||||||
|  |         if (name.isNullOrBlank()) { | ||||||
|  |             name = System.getenv("HOSTNAME") | ||||||
|  |         } | ||||||
|  |         if (name.isNullOrBlank()) { | ||||||
|  |             name = InetAddress.getLocalHost().hostName | ||||||
|  |         } | ||||||
|  |         name ?: "unknown" | ||||||
|  |     } catch (e: Exception) { | ||||||
|  |         "unknown" | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取当前用户名 | ||||||
|  |  * | ||||||
|  |  * @return 当前用户名,如果无法获取则返回"unknown" | ||||||
|  |  */ | ||||||
|  | val userName: String by lazy { | ||||||
|  |     try { | ||||||
|  |         getEnvVar("USERNAME") | ||||||
|  |             ?: getEnvVar("USER") | ||||||
|  |             ?: System.getProperty("user.name") | ||||||
|  |             ?: "unknown" | ||||||
|  |     } catch (e: SecurityException) { | ||||||
|  |         "unknown" | ||||||
|  |     } catch (e: Exception) { | ||||||
|  |         "unknown" | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | private fun getEnvVar(name: String): String? { | ||||||
|  |     return try { | ||||||
|  |         System.getenv(name)?.takeIf { it.isNotBlank() } | ||||||
|  |     } catch (e: SecurityException) { | ||||||
|  |         null | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取本机MAC地址的字节数组形式 | ||||||
|  |  * | ||||||
|  |  * @return MAC地址字节数组,如果无法获取则返回空数组 | ||||||
|  |  */ | ||||||
|  | val macAddressBytes: ByteArray by lazy { | ||||||
|  |     try { | ||||||
|  |         val interfaces = NetworkInterface.getNetworkInterfaces() | ||||||
|  | 
 | ||||||
|  |         while (interfaces.hasMoreElements()) { | ||||||
|  |             val networkInterface = interfaces.nextElement() | ||||||
|  | 
 | ||||||
|  |             // 跳过回环接口和虚拟接口 | ||||||
|  |             if (networkInterface.isLoopback || networkInterface.isVirtual || !networkInterface.isUp) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             val mac = networkInterface.hardwareAddress | ||||||
|  |             if (mac != null && mac.isNotEmpty()) { | ||||||
|  |                 return@lazy mac | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         randomByteSecure(6) | ||||||
|  |     } catch (e: SocketException) { | ||||||
|  |         randomByteSecure(6) | ||||||
|  |     } catch (e: Exception) { | ||||||
|  |         randomByteSecure(6) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取本机MAC地址的十六进制字符串列表形式 | ||||||
|  |  * | ||||||
|  |  * @return MAC地址字符串列表,每个元素表示一个字节的十六进制值(大写),如果无法获取则返回空列表 | ||||||
|  |  */ | ||||||
|  | val macAddressStringList: List<String> by lazy { | ||||||
|  |     val macBytes = macAddressBytes | ||||||
|  |     if (macBytes.isEmpty()) { | ||||||
|  |         return@lazy emptyList() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     macBytes.map { String.format("%02X", it) } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取本机MAC地址的格式化字符串形式(如 "00:11:22:33:44:55") | ||||||
|  |  * | ||||||
|  |  * @return 格式化的MAC地址字符串,如果无法获取则返回空字符串 | ||||||
|  |  */ | ||||||
|  | val macAddressFormattedString: String by lazy { | ||||||
|  |     val macBytes = macAddressBytes | ||||||
|  |     if (macBytes.isEmpty()) { | ||||||
|  |         return@lazy "" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     macBytes.joinToString(":") { String.format("%02X", it) } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取所有网络接口的MAC地址映射 | ||||||
|  |  * | ||||||
|  |  * @return Map结构,key为网络接口名称,value为对应的MAC地址字节数组 | ||||||
|  |  */ | ||||||
|  | val allMacAddresses: Map<String, ByteArray> by lazy { | ||||||
|  |     try { | ||||||
|  |         val result = mutableMapOf<String, ByteArray>() | ||||||
|  |         val interfaces = NetworkInterface.getNetworkInterfaces() | ||||||
|  | 
 | ||||||
|  |         while (interfaces.hasMoreElements()) { | ||||||
|  |             val networkInterface = interfaces.nextElement() | ||||||
|  | 
 | ||||||
|  |             // 跳过回环接口和虚拟接口 | ||||||
|  |             if (networkInterface.isLoopback || networkInterface.isVirtual) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             val mac = networkInterface.hardwareAddress | ||||||
|  |             if (mac != null && mac.isNotEmpty()) { | ||||||
|  |                 result[networkInterface.name] = mac | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         result | ||||||
|  |     } catch (e: SocketException) { | ||||||
|  |         emptyMap() | ||||||
|  |     } catch (e: Exception) { | ||||||
|  |         emptyMap() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取所有网络接口的MAC地址字符串列表映射 | ||||||
|  |  * | ||||||
|  |  * @return Map结构,key为网络接口名称,value为对应的MAC地址字符串列表 | ||||||
|  |  */ | ||||||
|  | val allMacAddressesStringList: Map<String, List<String>> by lazy { | ||||||
|  |     allMacAddresses.mapValues { entry -> | ||||||
|  |         entry.value.map { String.format("%02X", it) } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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 09:57:50 |  * LastUpdate 2025-09-17 19:06:39 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| @ -27,8 +27,8 @@ import com.mingliqiye.utils.jna.NANOS_PER_100NS | |||||||
| import com.mingliqiye.utils.jna.WinKernel32Api | import com.mingliqiye.utils.jna.WinKernel32Api | ||||||
| import com.mingliqiye.utils.jna.getWinKernel32Apis | import com.mingliqiye.utils.jna.getWinKernel32Apis | ||||||
| import com.mingliqiye.utils.logger.mingLiLoggerFactory | import com.mingliqiye.utils.logger.mingLiLoggerFactory | ||||||
| import com.mingliqiye.utils.system.getJavaVersionAsInteger |  | ||||||
| import com.mingliqiye.utils.system.isWindows | import com.mingliqiye.utils.system.isWindows | ||||||
|  | import com.mingliqiye.utils.system.javaVersionAsInteger | ||||||
| import org.slf4j.Logger | import org.slf4j.Logger | ||||||
| import java.io.Serializable | import java.io.Serializable | ||||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||||
| @ -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) { | ||||||
|             getJavaVersionAsInteger() == 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) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ | |||||||
|  * ProjectName mingli-utils |  * ProjectName mingli-utils | ||||||
|  * ModuleName mingli-utils.main |  * ModuleName mingli-utils.main | ||||||
|  * CurrentFile MysqlUUIDv1.kt |  * CurrentFile MysqlUUIDv1.kt | ||||||
|  * LastUpdate 2025-09-14 18:19:29 |  * LastUpdate 2025-09-15 22:32:50 | ||||||
|  * UpdateUser MingLiPro |  * UpdateUser MingLiPro | ||||||
|  */ |  */ | ||||||
| @file:JvmName("MysqlUUIDv1") | @file:JvmName("MysqlUUIDv1") | ||||||
|  | |||||||
| @ -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-14 22:38:51 |  * 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,34 +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 { |  | ||||||
|         return uuid == other |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 计算此 UUID 的哈希码。 |  | ||||||
|      * |  | ||||||
|      * @return 哈希码值 |  | ||||||
|      */ |  | ||||||
|     override fun hashCode(): Int { |  | ||||||
|         return uuid.hashCode() |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ | |||||||
| # ProjectName mingli-utils | # ProjectName mingli-utils | ||||||
| # ModuleName mingli-utils.main | # ModuleName mingli-utils.main | ||||||
| # CurrentFile org.springframework.boot.autoconfigure.AutoConfiguration.imports | # CurrentFile org.springframework.boot.autoconfigure.AutoConfiguration.imports | ||||||
| # LastUpdate 2025-09-09 08:37:33 | # LastUpdate 2025-09-15 22:32:50 | ||||||
| # UpdateUser MingLiPro | # UpdateUser MingLiPro | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user