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
|
||||
*lock*
|
||||
.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
|
||||
* ModuleName mingli-utils
|
||||
* CurrentFile build.gradle.kts
|
||||
* LastUpdate 2025-09-15 11:20:04
|
||||
* LastUpdate 2025-09-21 15:36:59
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -26,8 +26,9 @@ import java.time.format.DateTimeFormatter
|
||||
plugins {
|
||||
idea
|
||||
java
|
||||
id("java-library")
|
||||
id("maven-publish")
|
||||
signing
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
kotlin("jvm") version "2.2.20"
|
||||
id("org.jetbrains.dokka") version "2.0.0"
|
||||
}
|
||||
@ -64,23 +65,21 @@ java {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor("org.jetbrains:annotations:24.0.0")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.38")
|
||||
|
||||
implementation("org.slf4j:slf4j-api:2.0.17")
|
||||
|
||||
implementation("com.mingliqiye.utils.jna:WinKernel32Api:1.0.1")
|
||||
// https://github.com/jeremyh/jBCrypt
|
||||
implementation("org.mindrot:jbcrypt:0.4")
|
||||
|
||||
compileOnly("org.springframework.boot:spring-boot-starter:2.7.14")
|
||||
compileOnly("com.fasterxml.jackson.core:jackson-databind:2.19.2")
|
||||
compileOnly("com.google.code.gson:gson:2.13.1")
|
||||
compileOnly("org.mybatis:mybatis:3.5.19")
|
||||
compileOnly("com.alibaba.fastjson2:fastjson2:2.0.58")
|
||||
compileOnly("org.projectlombok:lombok:1.18.38")
|
||||
implementation("org.bouncycastle:bcprov-jdk18on:1.81")
|
||||
implementation("com.github.f4b6a3:uuid-creator:6.1.0")
|
||||
implementation("org.mindrot:jbcrypt:0.4")
|
||||
implementation("org.jetbrains:annotations:24.0.0")
|
||||
compileOnly("net.java.dev.jna:jna:5.17.0")
|
||||
//implementation("jakarta.annotation:jakarta.annotation-api:2.1.1")
|
||||
implementation("org.slf4j:slf4j-api:2.0.17")
|
||||
implementation("com.mingliqiye.utils.jna:WinKernel32Api:1.0.1")
|
||||
|
||||
compileOnly("com.baomidou:mybatis-plus-core:3.0.1")
|
||||
compileOnly("net.java.dev.jna:jna:5.17.0")
|
||||
}
|
||||
|
||||
|
||||
@ -97,10 +96,12 @@ tasks.withType<JavaExec>().configureEach {
|
||||
|
||||
tasks.withType<org.gradle.jvm.tasks.Jar> {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from("LICENSE") { into("META-INF") }
|
||||
from("NOTICE") { into("META-INF") }
|
||||
manifest {
|
||||
attributes(
|
||||
mapOf(
|
||||
"Main-Class" to "com.mingliqiye.utils.Main",
|
||||
"Main-Class" to "com.mingliqiye.utils.main.Main",
|
||||
"Specification-Title" to ARTIFACTID,
|
||||
"Specification-Version" to VERSIONS,
|
||||
"Specification-Vendor" to "minglipro",
|
||||
@ -131,17 +132,16 @@ repositories {
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
tasks.register<Jar>("javaDocJar") {
|
||||
group = "build"
|
||||
archiveClassifier.set("javadoc")
|
||||
dependsOn(tasks.dokkaJavadoc)
|
||||
dependsOn("dokkaJavadoc")
|
||||
from(buildDir.resolve("dokka/javadoc"))
|
||||
}
|
||||
tasks.register<Jar>("kotlinDocJar") {
|
||||
group = "build"
|
||||
archiveClassifier.set("kotlindoc")
|
||||
dependsOn(tasks.dokkaHtml)
|
||||
dependsOn("dokkaHtml")
|
||||
from(buildDir.resolve("dokka/html"))
|
||||
}
|
||||
publishing {
|
||||
@ -150,6 +150,10 @@ publishing {
|
||||
name = "MavenRepositoryRaw"
|
||||
url = uri("C:/data/git/maven-repository-raw")
|
||||
}
|
||||
maven {
|
||||
name = "OSSRepository"
|
||||
url = uri("C:/data/git/maven-repository-raw-utils")
|
||||
}
|
||||
}
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
@ -158,8 +162,34 @@ publishing {
|
||||
artifact(tasks.named("kotlinDocJar"))
|
||||
artifactId = ARTIFACTID
|
||||
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))
|
||||
pom {
|
||||
name = "mingli-utils"
|
||||
url = "https://mingli-utils.mingliqiye.com"
|
||||
description = "A Java/kotlin Utils"
|
||||
licenses {
|
||||
license {
|
||||
name = "The Apache License, Version 2.0"
|
||||
url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = "minglipro"
|
||||
name = "mingli"
|
||||
email = "minglipro@163.com"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection = "scm:git:https://git.mingliqiye.com/minglipro/mingli-utils.git"
|
||||
developerConnection = "scm:git:https://git.mingliqiye.com:minglipro/mingli-utils.git"
|
||||
url = "https://git.mingliqiye.com/minglipro/mingli-utils"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
signing {
|
||||
sign(publishing.publications)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
@ -179,7 +209,6 @@ tasks.processResources {
|
||||
DateTimeFormatter.ofPattern(
|
||||
"yyyy-MM-dd HH:mm:ss.SSSSSSS"
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -16,10 +16,13 @@
|
||||
# ProjectName mingli-utils
|
||||
# ModuleName mingli-utils
|
||||
# CurrentFile gradle.properties
|
||||
# LastUpdate 2025-09-15 17:24:10
|
||||
# LastUpdate 2025-09-21 15:38:52
|
||||
# UpdateUser MingLiPro
|
||||
#
|
||||
JDKVERSIONS=1.8
|
||||
GROUPSID=com.mingliqiye.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
|
||||
# ModuleName mingli-utils
|
||||
# CurrentFile gradle-wrapper.properties
|
||||
# LastUpdate 2025-09-15 12:01:36
|
||||
# LastUpdate 2025-09-15 22:32:50
|
||||
# UpdateUser MingLiPro
|
||||
#
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
|
||||
2
gradlew
vendored
2
gradlew
vendored
@ -18,7 +18,7 @@
|
||||
# ProjectName mingli-utils
|
||||
# ModuleName mingli-utils
|
||||
# CurrentFile gradlew
|
||||
# LastUpdate 2025-09-15 12:01:36
|
||||
# LastUpdate 2025-09-15 22:32:50
|
||||
# UpdateUser MingLiPro
|
||||
#
|
||||
|
||||
|
||||
@ -16,12 +16,16 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.jdk8
|
||||
* CurrentFile build.gradle.kts
|
||||
* LastUpdate 2025-09-14 18:19:04
|
||||
* LastUpdate 2025-09-21 15:39:12
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("maven-publish")
|
||||
signing
|
||||
kotlin("jvm") version "2.2.20"
|
||||
id("org.jetbrains.dokka") version "2.0.0"
|
||||
}
|
||||
val GROUPSID = project.properties["GROUPSID"] as String
|
||||
val VERSIONS = project.properties["VERSIONS"] as String
|
||||
@ -39,15 +43,44 @@ publishing {
|
||||
name = "MavenRepositoryRaw"
|
||||
url = uri("C:/data/git/maven-repository-raw")
|
||||
}
|
||||
maven {
|
||||
name = "OSSRepository"
|
||||
url = uri("C:/data/git/maven-repository-raw-utils")
|
||||
}
|
||||
}
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
from(components["java"])
|
||||
artifactId = "$ARTIFACTID-win-jdk8"
|
||||
groupId = GROUPSID
|
||||
version = VERSIONS
|
||||
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))
|
||||
pom {
|
||||
name = "mingli-utils-win-jdk8"
|
||||
url = "https://mingli-utils.mingliqiye.com"
|
||||
description = "A Java/kotlin Utils"
|
||||
licenses {
|
||||
license {
|
||||
name = "The Apache License, Version 2.0"
|
||||
url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = "minglipro"
|
||||
name = "mingli"
|
||||
email = "minglipro@163.com"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection = "scm:git:https://git.mingliqiye.com/minglipro/mingli-utils.git"
|
||||
developerConnection = "scm:git:https://git.mingliqiye.com:minglipro/mingli-utils.git"
|
||||
url = "https://git.mingliqiye.com/minglipro/mingli-utils"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
signing {
|
||||
sign(publishing.publications)
|
||||
}
|
||||
}
|
||||
|
||||
java.toolchain.languageVersion.set(JavaLanguageVersion.of(8))
|
||||
|
||||
23
jdk8/gradle.properties
Normal file
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
|
||||
* ModuleName mingli-utils
|
||||
* CurrentFile settings.gradle.kts
|
||||
* LastUpdate 2025-09-13 02:37:04
|
||||
* LastUpdate 2025-09-16 12:32:52
|
||||
* 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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile PlayerSample.java
|
||||
* LastUpdate 2025-09-09 08:37:33
|
||||
* CurrentFile StreamEmptyException.java
|
||||
* LastUpdate 2025-09-20 13:24:07
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
package com.mingliqiye.utils.minecraft.slp;
|
||||
package com.mingliqiye.utils.stream;
|
||||
|
||||
import lombok.Data;
|
||||
public class StreamEmptyException extends java.lang.RuntimeException {
|
||||
|
||||
@Data
|
||||
public class PlayerSample {
|
||||
public StreamEmptyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
private String name;
|
||||
private String id;
|
||||
public StreamEmptyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile AesUtils.kt
|
||||
* LastUpdate 2025-09-14 18:43:04
|
||||
* LastUpdate 2025-09-19 21:35:53
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -25,8 +25,7 @@
|
||||
|
||||
package com.mingliqiye.utils.aes
|
||||
|
||||
import com.mingliqiye.utils.base64.decode
|
||||
import com.mingliqiye.utils.base64.encode
|
||||
import com.mingliqiye.utils.base.BASE64
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.GeneralSecurityException
|
||||
import java.security.MessageDigest
|
||||
@ -72,8 +71,8 @@ fun encrypt(sSrc: String, sKey: String?): String? {
|
||||
val encrypted = cipher.doFinal(
|
||||
sSrc.toByteArray(StandardCharsets.UTF_8)
|
||||
)
|
||||
return encode(
|
||||
"${encode(iv)}:${encode(encrypted)}".toByteArray()
|
||||
return BASE64.encode(
|
||||
"${BASE64.encode(iv)}:${BASE64.encode(encrypted)}".toByteArray()
|
||||
)
|
||||
}
|
||||
|
||||
@ -86,13 +85,13 @@ fun encrypt(sSrc: String, sKey: String?): String? {
|
||||
fun decrypt(sSrc: String, sKey: String): String? {
|
||||
try {
|
||||
// 分割IV和加密数据
|
||||
val sSrcs = String(decode(sSrc))
|
||||
val sSrcs = String(BASE64.decode(sSrc))
|
||||
val parts: Array<String?> = sSrcs.split(":".toRegex(), limit = 2).toTypedArray()
|
||||
if (parts.size != 2) {
|
||||
return null
|
||||
}
|
||||
val iv = decode(parts[0]!!)
|
||||
val encryptedData = decode(parts[1]!!)
|
||||
val iv = BASE64.decode(parts[0]!!)
|
||||
val encryptedData = BASE64.decode(parts[1]!!)
|
||||
if (iv.size != GCM_IV_LENGTH) {
|
||||
return null
|
||||
}
|
||||
@ -122,3 +121,4 @@ private fun createSecretKey(sKey: String): SecretKeySpec {
|
||||
val digest = md.digest(key)
|
||||
return SecretKeySpec(digest, ALGORITHM)
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile ByteUtils.kt
|
||||
* LastUpdate 2025-09-15 17:26:34
|
||||
* LastUpdate 2025-09-20 11:49:05
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
@file:JvmName("ByteUtils")
|
||||
|
||||
package com.mingliqiye.utils.bytes
|
||||
|
||||
import com.mingliqiye.utils.stream.SuperStream
|
||||
|
||||
const val ESC_ASC: Byte = 0x10
|
||||
const val ESC_DESC: Byte = 0x1B
|
||||
const val ESC_NONE: Byte = 0x00
|
||||
@ -39,8 +41,20 @@ const val ESC_RESERVED: Byte = 0x06
|
||||
* @return 包含每个字节对应十六进制字符串的列表
|
||||
*/
|
||||
fun ByteArray.getByteArrayString(): MutableList<String> {
|
||||
return this.toList().stream()
|
||||
.map { a -> String.format("0X%02X", a!!.toInt() and 0xFF) }
|
||||
.collect(com.mingliqiye.utils.stream.toList()) as MutableList<String>
|
||||
return this.toList().stream().map { a -> String.format("0X%02X", a!!.toInt() and 0xFF) }
|
||||
.collect(SuperStream.toList()) as MutableList<String>
|
||||
}
|
||||
|
||||
|
||||
fun Char.hexDigitToValue(): Int {
|
||||
return when (this) {
|
||||
in '0'..'9' -> this - '0'
|
||||
in 'A'..'F' -> this - 'A' + 10
|
||||
in 'a'..'f' -> this - 'a' + 10
|
||||
else -> throw NumberFormatException("Invalid hex character: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun hexStringToByteArray(string: String): ByteArray {
|
||||
return string.hexToByteArray()
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile CloneUtils.kt
|
||||
* LastUpdate 2025-09-15 09:30:37
|
||||
* LastUpdate 2025-09-20 14:01:29
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
@file:JvmName("CloneUtils")
|
||||
@ -38,8 +38,7 @@ inline fun <reified T> T.deepJsonClone(jsonApi: JsonApi): T {
|
||||
|
||||
} catch (e: Exception) {
|
||||
throw JsonException(
|
||||
"Failed to deep clone object using JSON",
|
||||
e
|
||||
"Failed to deep clone object using JSON", e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile Collection.kt
|
||||
* LastUpdate 2025-09-15 17:26:00
|
||||
* LastUpdate 2025-09-21 14:36:57
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -24,22 +24,48 @@
|
||||
|
||||
package com.mingliqiye.utils.collection
|
||||
|
||||
import com.mingliqiye.utils.stream.SuperStream
|
||||
import java.util.*
|
||||
import java.util.stream.Collectors
|
||||
|
||||
|
||||
/**
|
||||
* 将当前集合转换为数组。
|
||||
*
|
||||
* @param T 集合元素类型
|
||||
* @return 转换后的数组
|
||||
*/
|
||||
inline fun <reified T> Collection<T>.toArray(): Array<T> {
|
||||
return arrayOf(*this.toTypedArray())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前集合转换为 Map,其中键为集合元素本身,值由给定函数生成。
|
||||
*
|
||||
* @param T 集合元素类型
|
||||
* @param V 映射值的类型
|
||||
* @param v 用于生成映射值的函数
|
||||
* @return 转换后的 Map
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <reified T, V> Collection<T>.toMap(noinline v: (T) -> V): Map<T, V> {
|
||||
return this.stream().collect(
|
||||
com.mingliqiye.utils.stream.toMapValueThis(
|
||||
SuperStream.toMap(
|
||||
v
|
||||
)
|
||||
)
|
||||
) as Map<T, V>
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前集合转换为 Map,其中键和值分别由给定函数生成。
|
||||
*
|
||||
* @param T 集合元素类型
|
||||
* @param V 映射值的类型
|
||||
* @param K 映射键的类型
|
||||
* @param k 用于生成映射键的函数
|
||||
* @param v 用于生成映射值的函数
|
||||
* @return 转换后的 Map
|
||||
*/
|
||||
inline fun <reified T, V, K> Collection<T>.toMap(noinline k: (T) -> K, noinline v: (T) -> V): Map<K, V> {
|
||||
return this.stream().collect(
|
||||
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> {
|
||||
return array.toSet()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取集合中的第一个元素,如果集合为空则返回 null。
|
||||
*
|
||||
* @param T 集合元素类型
|
||||
* @return 第一个元素或 null
|
||||
*/
|
||||
inline fun <reified T> Collection<T>.getFirst(): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
@ -63,6 +102,12 @@ inline fun <reified T> Collection<T>.getFirst(): T? {
|
||||
return this.iterator().next()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数组中的第一个元素,如果数组为空则返回 null。
|
||||
*
|
||||
* @param T 数组元素类型
|
||||
* @return 第一个元素或 null
|
||||
*/
|
||||
inline fun <reified T> Array<T>.getFirst(): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
@ -70,6 +115,12 @@ inline fun <reified T> Array<T>.getFirst(): T? {
|
||||
return this.first()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取集合中的最后一个元素,如果集合为空则返回 null。
|
||||
*
|
||||
* @param T 集合元素类型
|
||||
* @return 最后一个元素或 null
|
||||
*/
|
||||
inline fun <reified T> Collection<T>.getLast(): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
@ -84,6 +135,12 @@ inline fun <reified T> Collection<T>.getLast(): T? {
|
||||
return lastElement
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数组中的最后一个元素,如果数组为空则返回 null。
|
||||
*
|
||||
* @param T 数组元素类型
|
||||
* @return 最后一个元素或 null
|
||||
*/
|
||||
inline fun <reified T> Array<T>.getLast(): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
@ -92,6 +149,14 @@ inline fun <reified T> Array<T>.getLast(): T? {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据索引获取集合中的元素,若索引越界则返回默认值。
|
||||
*
|
||||
* @param T 集合元素类型
|
||||
* @param index 索引位置
|
||||
* @param defaultValue 默认返回值
|
||||
* @return 指定索引处的元素或默认值
|
||||
*/
|
||||
inline fun <reified T> Collection<T>.getOrDefault(
|
||||
index: Int, defaultValue: T
|
||||
): T {
|
||||
@ -111,54 +176,135 @@ inline fun <reified T> Collection<T>.getOrDefault(
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> newArrayList(): ArrayList<T> {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个指定初始容量的新 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param size 初始容量大小
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> newArrayList(size: Int): ArrayList<T> {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> newArrayList(vararg elements: T): ArrayList<T> {
|
||||
return newArrayList(elements.asList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前集合转换为新的 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> Collection<T>.newArrayLists(): ArrayList<T> {
|
||||
return newArrayList(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数组转换为新的 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入数组
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> newArrayLists(elements: Array<T>): ArrayList<T> {
|
||||
return newArrayList(elements.asList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> newArrayList(elements: Collection<T>): ArrayList<T> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Iterable 转换为新的 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Iterable
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> newArrayList(elements: Iterable<T>): ArrayList<T> {
|
||||
return newArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Sequence 转换为新的 ArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Sequence
|
||||
* @return 新创建的 ArrayList
|
||||
*/
|
||||
fun <T> newArrayList(elements: Sequence<T>): ArrayList<T> {
|
||||
return newArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 LinkedList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 LinkedList
|
||||
*/
|
||||
fun <T> newLinkedList(): LinkedList<T> {
|
||||
return LinkedList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 LinkedList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 LinkedList
|
||||
*/
|
||||
fun <T> newLinkedList(vararg elements: T): LinkedList<T> {
|
||||
val list = newLinkedList<T>()
|
||||
list.addAll(elements.asList())
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 LinkedList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 LinkedList
|
||||
*/
|
||||
fun <T> newLinkedList(elements: Collection<T>): LinkedList<T> {
|
||||
val list = newLinkedList<T>()
|
||||
list.addAll(elements)
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Iterable 转换为新的 LinkedList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Iterable
|
||||
* @return 新创建的 LinkedList
|
||||
*/
|
||||
fun <T> newLinkedList(elements: Iterable<T>): LinkedList<T> {
|
||||
val list = newLinkedList<T>()
|
||||
for (element in elements) {
|
||||
@ -167,26 +313,60 @@ fun <T> newLinkedList(elements: Iterable<T>): LinkedList<T> {
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Sequence 转换为新的 LinkedList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Sequence
|
||||
* @return 新创建的 LinkedList
|
||||
*/
|
||||
fun <T> newLinkedList(elements: Sequence<T>): LinkedList<T> {
|
||||
return newLinkedList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 Vector 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 Vector
|
||||
*/
|
||||
fun <T> newVector(): Vector<T> {
|
||||
return Vector()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 Vector 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 Vector
|
||||
*/
|
||||
fun <T> newVector(vararg elements: T): Vector<T> {
|
||||
val vector = newVector<T>()
|
||||
vector.addAll(elements.asList())
|
||||
return vector
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 Vector 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 Vector
|
||||
*/
|
||||
fun <T> newVector(elements: Collection<T>): Vector<T> {
|
||||
val vector = newVector<T>()
|
||||
vector.addAll(elements)
|
||||
return vector
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Iterable 转换为新的 Vector 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Iterable
|
||||
* @return 新创建的 Vector
|
||||
*/
|
||||
fun <T> newVector(elements: Iterable<T>): Vector<T> {
|
||||
val vector = newVector<T>()
|
||||
for (element in elements) {
|
||||
@ -195,31 +375,72 @@ fun <T> newVector(elements: Iterable<T>): Vector<T> {
|
||||
return vector
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Sequence 转换为新的 Vector 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Sequence
|
||||
* @return 新创建的 Vector
|
||||
*/
|
||||
fun <T> newVector(elements: Sequence<T>): Vector<T> {
|
||||
return newVector(elements.toList())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建一个新的 HashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 HashSet
|
||||
*/
|
||||
fun <T> newHashSet(): HashSet<T> {
|
||||
return HashSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个指定初始容量的新 HashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param size 初始容量大小
|
||||
* @return 新创建的 HashSet
|
||||
*/
|
||||
fun <T> newHashSet(size: Int): HashSet<T> {
|
||||
return HashSet(size)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 HashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 HashSet
|
||||
*/
|
||||
fun <T> newHashSet(vararg elements: T): HashSet<T> {
|
||||
val set = newHashSet<T>()
|
||||
set.addAll(elements.asList())
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 HashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 HashSet
|
||||
*/
|
||||
fun <T> newHashSet(elements: Collection<T>): HashSet<T> {
|
||||
val set = newHashSet<T>()
|
||||
set.addAll(elements)
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Iterable 转换为新的 HashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Iterable
|
||||
* @return 新创建的 HashSet
|
||||
*/
|
||||
fun <T> newHashSet(elements: Iterable<T>): HashSet<T> {
|
||||
val set = newHashSet<T>()
|
||||
for (element in elements) {
|
||||
@ -228,30 +449,71 @@ fun <T> newHashSet(elements: Iterable<T>): HashSet<T> {
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Sequence 转换为新的 HashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Sequence
|
||||
* @return 新创建的 HashSet
|
||||
*/
|
||||
fun <T> newHashSet(elements: Sequence<T>): HashSet<T> {
|
||||
return newHashSet(elements.toSet())
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 LinkedHashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 LinkedHashSet
|
||||
*/
|
||||
fun <T> newLinkedHashSet(): LinkedHashSet<T> {
|
||||
return LinkedHashSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个指定初始容量的新 LinkedHashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param size 初始容量大小
|
||||
* @return 新创建的 LinkedHashSet
|
||||
*/
|
||||
fun <T> newLinkedHashSet(size: Int): LinkedHashSet<T> {
|
||||
return LinkedHashSet(size)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 LinkedHashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 LinkedHashSet
|
||||
*/
|
||||
fun <T> newLinkedHashSet(vararg elements: T): LinkedHashSet<T> {
|
||||
val set = newLinkedHashSet<T>()
|
||||
set.addAll(elements.asList())
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 LinkedHashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 LinkedHashSet
|
||||
*/
|
||||
fun <T> newLinkedHashSet(elements: Collection<T>): LinkedHashSet<T> {
|
||||
val set = newLinkedHashSet<T>()
|
||||
set.addAll(elements)
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Iterable 转换为新的 LinkedHashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Iterable
|
||||
* @return 新创建的 LinkedHashSet
|
||||
*/
|
||||
fun <T> newLinkedHashSet(elements: Iterable<T>): LinkedHashSet<T> {
|
||||
val set = newLinkedHashSet<T>()
|
||||
for (element in elements) {
|
||||
@ -260,26 +522,60 @@ fun <T> newLinkedHashSet(elements: Iterable<T>): LinkedHashSet<T> {
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Sequence 转换为新的 LinkedHashSet 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入 Sequence
|
||||
* @return 新创建的 LinkedHashSet
|
||||
*/
|
||||
fun <T> newLinkedHashSet(elements: Sequence<T>): LinkedHashSet<T> {
|
||||
return newLinkedHashSet(elements.toSet())
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 TreeSet 实例。
|
||||
*
|
||||
* @param T 元素类型,必须实现 Comparable 接口
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T : Comparable<T>> newTreeSet(): TreeSet<T> {
|
||||
return TreeSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 TreeSet 实例。
|
||||
*
|
||||
* @param T 元素类型,必须实现 Comparable 接口
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T : Comparable<T>> newTreeSet(vararg elements: T): TreeSet<T> {
|
||||
val set = newTreeSet<T>()
|
||||
set.addAll(elements.asList())
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 TreeSet 实例。
|
||||
*
|
||||
* @param T 元素类型,必须实现 Comparable 接口
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T : Comparable<T>> newTreeSet(elements: Collection<T>): TreeSet<T> {
|
||||
val set = newTreeSet<T>()
|
||||
set.addAll(elements)
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Iterable 转换为新的 TreeSet 实例。
|
||||
*
|
||||
* @param T 元素类型,必须实现 Comparable 接口
|
||||
* @param elements 输入 Iterable
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T : Comparable<T>> newTreeSet(elements: Iterable<T>): TreeSet<T> {
|
||||
val set = newTreeSet<T>()
|
||||
for (element in elements) {
|
||||
@ -288,65 +584,160 @@ fun <T : Comparable<T>> newTreeSet(elements: Iterable<T>): TreeSet<T> {
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Sequence 转换为新的 TreeSet 实例。
|
||||
*
|
||||
* @param T 元素类型,必须实现 Comparable 接口
|
||||
* @param elements 输入 Sequence
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T : Comparable<T>> newTreeSet(elements: Sequence<T>): TreeSet<T> {
|
||||
return newTreeSet(elements.toSet())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将字节数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入字节数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: ByteArray): ArrayList<Byte> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将短整型数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入短整型数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: ShortArray): ArrayList<Short> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将整型数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入整型数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: IntArray): ArrayList<Int> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将长整型数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入长整型数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: LongArray): ArrayList<Long> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将浮点数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入浮点数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: FloatArray): ArrayList<Float> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将双精度浮点数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入双精度浮点数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: DoubleArray): ArrayList<Double> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将布尔数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入布尔数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: BooleanArray): ArrayList<Boolean> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符数组转换为 ArrayList。
|
||||
*
|
||||
* @param elements 输入字符数组
|
||||
* @return 转换后的 ArrayList
|
||||
*/
|
||||
fun newArrayLists(elements: CharArray): ArrayList<Char> {
|
||||
return ArrayList(elements.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 CopyOnWriteArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 CopyOnWriteArrayList
|
||||
*/
|
||||
fun <T> newCopyOnWriteArrayList(): java.util.concurrent.CopyOnWriteArrayList<T> {
|
||||
return java.util.concurrent.CopyOnWriteArrayList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 CopyOnWriteArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 CopyOnWriteArrayList
|
||||
*/
|
||||
fun <T> newCopyOnWriteArrayList(vararg elements: T): java.util.concurrent.CopyOnWriteArrayList<T> {
|
||||
return java.util.concurrent.CopyOnWriteArrayList(elements.asList())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 CopyOnWriteArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 CopyOnWriteArrayList
|
||||
*/
|
||||
fun <T> newCopyOnWriteArrayList(elements: Collection<T>): java.util.concurrent.CopyOnWriteArrayList<T> {
|
||||
return java.util.concurrent.CopyOnWriteArrayList(elements)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 Stack 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 Stack
|
||||
*/
|
||||
fun <T> newStack(): Stack<T> {
|
||||
return Stack()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 Stack 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 Stack
|
||||
*/
|
||||
fun <T> newStack(vararg elements: T): Stack<T> {
|
||||
val stack = newStack<T>()
|
||||
stack.addAll(elements.asList())
|
||||
return stack
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 Stack 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 Stack
|
||||
*/
|
||||
fun <T> newStack(elements: Collection<T>): Stack<T> {
|
||||
val stack = newStack<T>()
|
||||
stack.addAll(elements)
|
||||
@ -354,22 +745,53 @@ fun <T> newStack(elements: Collection<T>): Stack<T> {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的 TreeSet 实例,并指定比较器。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param comparator 用于排序的比较器
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T> newTreeSet(comparator: Comparator<T>): TreeSet<T> {
|
||||
return TreeSet(comparator)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用可变参数创建一个新的 TreeSet 实例,并指定比较器。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param comparator 用于排序的比较器
|
||||
* @param elements 可变参数列表
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T> newTreeSet(comparator: Comparator<T>, vararg elements: T): TreeSet<T> {
|
||||
val set = newTreeSet(comparator)
|
||||
set.addAll(elements.asList())
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为新的 TreeSet 实例,并指定比较器。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param comparator 用于排序的比较器
|
||||
* @param elements 输入集合
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T> newTreeSet(comparator: Comparator<T>, elements: Collection<T>): TreeSet<T> {
|
||||
val set = newTreeSet(comparator)
|
||||
set.addAll(elements)
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Iterable 转换为新的 TreeSet 实例,并指定比较器。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param comparator 用于排序的比较器
|
||||
* @param elements 输入 Iterable
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T> newTreeSet(comparator: Comparator<T>, elements: Iterable<T>): TreeSet<T> {
|
||||
val set = newTreeSet(comparator)
|
||||
for (element in elements) {
|
||||
@ -378,32 +800,71 @@ fun <T> newTreeSet(comparator: Comparator<T>, elements: Iterable<T>): TreeSet<T>
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Sequence 转换为新的 TreeSet 实例,并指定比较器。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param comparator 用于排序的比较器
|
||||
* @param elements 输入 Sequence
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T> newTreeSet(comparator: Comparator<T>, elements: Sequence<T>): TreeSet<T> {
|
||||
return newTreeSet(comparator, elements.toSet())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前集合转换为新的 CopyOnWriteArrayList 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 CopyOnWriteArrayList
|
||||
*/
|
||||
fun <T> Collection<T>.newCopyOnWriteArrayLists(): java.util.concurrent.CopyOnWriteArrayList<T> {
|
||||
return java.util.concurrent.CopyOnWriteArrayList(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前集合转换为新的 Stack 实例。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @return 新创建的 Stack
|
||||
*/
|
||||
fun <T> Collection<T>.newStacks(): Stack<T> {
|
||||
val stack = Stack<T>()
|
||||
stack.addAll(this)
|
||||
return stack
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前集合转换为新的 TreeSet 实例。
|
||||
*
|
||||
* @param T 元素类型,必须实现 Comparable 接口
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T> Collection<T>.newTreeSets(): TreeSet<T> where T : Comparable<T> {
|
||||
val set = TreeSet<T>()
|
||||
set.addAll(this)
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前集合转换为新的 TreeSet 实例,并指定比较器。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param comparator 用于排序的比较器
|
||||
* @return 新创建的 TreeSet
|
||||
*/
|
||||
fun <T> Collection<T>.newTreeSets(comparator: Comparator<T>): TreeSet<T> {
|
||||
val set = TreeSet(comparator)
|
||||
set.addAll(this)
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Byte 类型的 List 转换为字节数组。
|
||||
*
|
||||
* @param list 输入的 Byte 列表
|
||||
* @return 转换后的字节数组
|
||||
*/
|
||||
fun toArray(list: List<Byte>): ByteArray {
|
||||
val arr = ByteArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -413,6 +874,12 @@ fun toArray(list: List<Byte>): ByteArray {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将 Short 类型的 List 转换为短整型数组。
|
||||
*
|
||||
* @param list 输入的 Short 列表
|
||||
* @return 转换后的短整型数组
|
||||
*/
|
||||
fun toArray(list: List<Short>): ShortArray {
|
||||
val arr = ShortArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -421,6 +888,12 @@ fun toArray(list: List<Short>): ShortArray {
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Int 类型的 List 转换为整型数组。
|
||||
*
|
||||
* @param list 输入的 Int 列表
|
||||
* @return 转换后的整型数组
|
||||
*/
|
||||
fun toArray(list: List<Int>): IntArray {
|
||||
val arr = IntArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -429,6 +902,12 @@ fun toArray(list: List<Int>): IntArray {
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Long 类型的 List 转换为长整型数组。
|
||||
*
|
||||
* @param list 输入的 Long 列表
|
||||
* @return 转换后的长整型数组
|
||||
*/
|
||||
fun toArray(list: List<Long>): LongArray {
|
||||
val arr = LongArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -437,6 +916,12 @@ fun toArray(list: List<Long>): LongArray {
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Float 类型的 List 转换为浮点数组。
|
||||
*
|
||||
* @param list 输入的 Float 列表
|
||||
* @return 转换后的浮点数组
|
||||
*/
|
||||
fun toArray(list: List<Float>): FloatArray {
|
||||
val arr = FloatArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -445,6 +930,12 @@ fun toArray(list: List<Float>): FloatArray {
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Double 类型的 List 转换为双精度浮点数组。
|
||||
*
|
||||
* @param list 输入的 Double 列表
|
||||
* @return 转换后的双精度浮点数组
|
||||
*/
|
||||
fun toArray(list: List<Double>): DoubleArray {
|
||||
val arr = DoubleArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -453,6 +944,12 @@ fun toArray(list: List<Double>): DoubleArray {
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Boolean 类型的 List 转换为布尔数组。
|
||||
*
|
||||
* @param list 输入的 Boolean 列表
|
||||
* @return 转换后的布尔数组
|
||||
*/
|
||||
fun toArray(list: List<Boolean>): BooleanArray {
|
||||
val arr = BooleanArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -461,6 +958,12 @@ fun toArray(list: List<Boolean>): BooleanArray {
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Char 类型的 List 转换为字符数组。
|
||||
*
|
||||
* @param list 输入的 Char 列表
|
||||
* @return 转换后的字符数组
|
||||
*/
|
||||
fun toArray(list: List<Char>): CharArray {
|
||||
val arr = CharArray(list.size)
|
||||
for (i in list.indices) {
|
||||
@ -468,3 +971,23 @@ fun toArray(list: List<Char>): CharArray {
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将任意类型的 List 转换为数组。
|
||||
*
|
||||
* @param T 元素类型
|
||||
* @param list 输入的 List
|
||||
* @return 转换后的数组
|
||||
*/
|
||||
inline fun <reified T> toArray(list: List<T>): Array<T> {
|
||||
if (list.isEmpty())
|
||||
return arrayOf<T>()
|
||||
return SuperStream.of<T>(list)
|
||||
.toArray(T::class.java)
|
||||
}
|
||||
|
||||
fun <T> toArray(list: List<T>, clazz: Class<T>): Array<T> {
|
||||
return SuperStream.of<T>(list)
|
||||
.toArray(clazz)
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile IsChanged.kt
|
||||
* LastUpdate 2025-09-15 09:30:37
|
||||
* LastUpdate 2025-09-19 20:17:07
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
|
||||
@ -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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile HashUtils.kt
|
||||
* LastUpdate 2025-09-15 09:38:04
|
||||
* LastUpdate 2025-09-19 20:24:33
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
@file:JvmName("HashUtils")
|
||||
|
||||
package com.mingliqiye.utils.hash
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.mindrot.jbcrypt.BCrypt
|
||||
|
||||
import com.mingliqiye.utils.base.BASE16
|
||||
import com.mingliqiye.utils.bcrypt.checkpw
|
||||
import com.mingliqiye.utils.bcrypt.hashpw
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.Security
|
||||
|
||||
private val _addProvider = run {
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算指定文件的哈希值。
|
||||
@ -74,15 +71,7 @@ fun calculateFileHash(file: File, algorithm: String): String {
|
||||
* @return 对应的十六进制字符串
|
||||
*/
|
||||
private fun bytesToHex(bytes: ByteArray): String {
|
||||
val hexString = StringBuilder(2 * bytes.size)
|
||||
for (b in bytes) {
|
||||
val hex = Integer.toHexString(0xff and b.toInt())
|
||||
if (hex.length == 1) {
|
||||
hexString.append('0')
|
||||
}
|
||||
hexString.append(hex)
|
||||
}
|
||||
return hexString.toString()
|
||||
return BASE16.encode(bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,7 +81,7 @@ private fun bytesToHex(bytes: ByteArray): String {
|
||||
* @return 加密后的 BCrypt 哈希字符串
|
||||
*/
|
||||
fun bcrypt(string: String): String {
|
||||
return BCrypt.hashpw(string, BCrypt.gensalt())
|
||||
return hashpw(string)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,5 +92,5 @@ fun bcrypt(string: String): String {
|
||||
* @return 如果匹配返回 true,否则返回 false
|
||||
*/
|
||||
fun checkBcrypt(string: String, bcrypted: String): Boolean {
|
||||
return BCrypt.checkpw(string, bcrypted)
|
||||
return checkpw(string, bcrypted)
|
||||
}
|
||||
|
||||
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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile FieldStructure.kt
|
||||
* LastUpdate 2025-09-14 18:19:29
|
||||
* LastUpdate 2025-09-15 22:32:50
|
||||
* 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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile JsonApi.kt
|
||||
* LastUpdate 2025-09-15 11:10:59
|
||||
* LastUpdate 2025-09-15 22:32:50
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -306,8 +306,8 @@ interface JsonApi {
|
||||
* @param <T> 泛型参数,表示List中元素的类型
|
||||
* @return 解析后的List集合
|
||||
</T> */
|
||||
fun <T> parseList(json: String, elementType: Class<T>): MutableList<T> {
|
||||
return parse<MutableList<T>>(json, JsonTypeUtils.listType<T>(elementType))
|
||||
fun <T> parseList(json: String, elementType: Class<T>): List<T> {
|
||||
return parse(json, type = listType(elementType))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -372,7 +372,7 @@ interface JsonApi {
|
||||
* @param path 节点路径(如:"user.name")
|
||||
* @return 节点值的字符串表示
|
||||
*/
|
||||
fun getNodeValue(json: String, path: String): String
|
||||
fun getNodeValue(json: String, path: String): String?
|
||||
|
||||
/**
|
||||
* 更新JSON字符串中指定路径节点的值
|
||||
|
||||
@ -15,19 +15,18 @@
|
||||
*
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile Players.java
|
||||
* LastUpdate 2025-09-09 08:37:33
|
||||
* CurrentFile JsonException.kt
|
||||
* LastUpdate 2025-09-15 22:32:50
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
package com.mingliqiye.utils.minecraft.slp;
|
||||
package com.mingliqiye.utils.json
|
||||
|
||||
import lombok.Data;
|
||||
class JsonException : RuntimeException {
|
||||
|
||||
@Data
|
||||
public class Players {
|
||||
constructor(message: String) : super(message)
|
||||
|
||||
private int max;
|
||||
private int online;
|
||||
private PlayerSample[] sample;
|
||||
constructor(message: String, cause: Throwable) : super(message, cause)
|
||||
|
||||
constructor(cause: Throwable) : this(cause.message ?: "", cause)
|
||||
}
|
||||
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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile JsonStringConverter.kt
|
||||
* LastUpdate 2025-09-15 11:03:53
|
||||
* LastUpdate 2025-09-17 19:09:17
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -35,7 +35,6 @@ import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import com.mingliqiye.utils.time.DateTime
|
||||
import com.mingliqiye.utils.time.DateTime.Companion.parse
|
||||
import com.mingliqiye.utils.time.Formatter
|
||||
import com.mingliqiye.utils.uuid.UUID
|
||||
import com.mingliqiye.utils.uuid.UUID.Companion.of
|
||||
import java.io.IOException
|
||||
@ -219,7 +218,7 @@ class DateTimeJsonConverter : JsonStringConverter<DateTime>() {
|
||||
if (obj == null) {
|
||||
return null
|
||||
}
|
||||
return obj.format(Formatter.STANDARD_DATETIME)
|
||||
return obj.format()
|
||||
}
|
||||
|
||||
override fun deConvert(obj: String?): DateTime? {
|
||||
@ -227,9 +226,7 @@ class DateTimeJsonConverter : JsonStringConverter<DateTime>() {
|
||||
return null
|
||||
}
|
||||
return parse(
|
||||
obj,
|
||||
Formatter.STANDARD_DATETIME_MILLISECOUND7,
|
||||
true
|
||||
obj
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile Loggers.kt
|
||||
* LastUpdate 2025-09-14 18:19:29
|
||||
* LastUpdate 2025-09-18 09:30:48
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -416,6 +416,6 @@ class MingLiLoggerFactory {
|
||||
}
|
||||
}
|
||||
|
||||
val mingLiLoggerFactory = MingLiLoggerFactory()
|
||||
val mingLiLoggerFactory: MingLiLoggerFactory by lazy { MingLiLoggerFactory() }
|
||||
|
||||
|
||||
|
||||
@ -16,18 +16,20 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile Main.kt
|
||||
* LastUpdate 2025-09-15 09:53:43
|
||||
* LastUpdate 2025-09-20 13:22:11
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
package com.mingliqiye.utils
|
||||
@file:JvmName("Main")
|
||||
|
||||
package com.mingliqiye.utils.main
|
||||
|
||||
import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration
|
||||
import com.mingliqiye.utils.time.DateTime
|
||||
import com.mingliqiye.utils.stream.SuperStream
|
||||
|
||||
fun main() {
|
||||
AutoConfiguration.printBanner()
|
||||
println(DateTime.now())
|
||||
}
|
||||
val data = SuperStream.of(Array(0) { 1 })
|
||||
|
||||
fun test() {
|
||||
|
||||
println(data)
|
||||
}
|
||||
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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile NetworkException.java
|
||||
* LastUpdate 2025-09-09 08:37:33
|
||||
* CurrentFile QueryWrapper.kt
|
||||
* LastUpdate 2025-09-20 14:21:44
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
package com.mingliqiye.utils.network;
|
||||
package com.mingliqiye.utils.mybatisplus
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper
|
||||
|
||||
/**
|
||||
* 网络异常类,用于处理网络相关的运行时异常
|
||||
* BaseMapperQuery接口扩展了BaseMapper,提供了通用的查询包装器功能
|
||||
*
|
||||
* @author MingLiPro
|
||||
* @param T 实体类类型
|
||||
*/
|
||||
public class NetworkException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 构造一个带有指定详细消息的网络异常
|
||||
*
|
||||
* @param message 异常的详细消息
|
||||
*/
|
||||
public NetworkException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个网络异常,指定原因异常
|
||||
*
|
||||
* @param e 导致此异常的原因异常
|
||||
*/
|
||||
public NetworkException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
interface BaseMapperQuery<T> : BaseMapper<T> {
|
||||
/**
|
||||
* 创建并返回一个新的QueryWrapper实例
|
||||
*
|
||||
* @return QueryWrapper<T> 返回类型化的查询包装器实例
|
||||
*/
|
||||
fun queryWrapper(): QueryWrapper<T> {
|
||||
return QueryWrapper<T>()
|
||||
}
|
||||
}
|
||||
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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile OsPath.kt
|
||||
* LastUpdate 2025-09-15 08:59:15
|
||||
* LastUpdate 2025-09-18 09:47:43
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -26,9 +26,8 @@ package com.mingliqiye.utils.path
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.nio.file.*
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
class OsPath private constructor(private val path: Path) : Path by path {
|
||||
|
||||
@ -58,48 +57,4 @@ class OsPath private constructor(private val path: Path) : Path by path {
|
||||
return OsPath(Paths.get(""))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getParent(): Path? {
|
||||
var parent = path.parent
|
||||
if (parent == null) {
|
||||
parent = path.toAbsolutePath().parent
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
override fun toRealPath(vararg options: LinkOption): Path {
|
||||
return OsPath(path.toRealPath(*options))
|
||||
}
|
||||
|
||||
override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>): WatchKey {
|
||||
return path.register(watcher, *events)
|
||||
}
|
||||
|
||||
override fun register(
|
||||
watcher: WatchService,
|
||||
events: Array<out WatchEvent.Kind<*>>,
|
||||
vararg modifiers: WatchEvent.Modifier
|
||||
): WatchKey {
|
||||
return path.register(watcher, events, *modifiers)
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<Path> {
|
||||
return path.iterator()
|
||||
}
|
||||
|
||||
override fun compareTo(other: Path): Int {
|
||||
return path.compareTo(other)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return path.toString()
|
||||
}
|
||||
|
||||
override fun forEach(action: Consumer<in Path>) {
|
||||
path.forEach(action)
|
||||
}
|
||||
|
||||
override fun spliterator(): Spliterator<Path> {
|
||||
return path.spliterator()
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,13 +16,15 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile RandomBytes.kt
|
||||
* LastUpdate 2025-09-15 09:54:33
|
||||
* LastUpdate 2025-09-16 17:42:26
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
@file:JvmName("RandomBytes")
|
||||
|
||||
package com.mingliqiye.utils.random
|
||||
|
||||
import java.security.SecureRandom
|
||||
|
||||
/**
|
||||
* 生成指定长度的随机字节数组
|
||||
* @param length 数组长度
|
||||
@ -78,3 +80,14 @@ fun randomByteNoHave(from: Byte, to: Byte): Byte {
|
||||
val randomValue = randomIntNoHave(fromInt, toInt)
|
||||
return (randomValue and 0xFF).toByte()
|
||||
}
|
||||
|
||||
val secureRandom: SecureRandom by lazy {
|
||||
SecureRandom.getInstanceStrong()
|
||||
}
|
||||
|
||||
|
||||
fun randomByteSecure(size: Int): ByteArray {
|
||||
val bytes = ByteArray(size)
|
||||
secureRandom.nextBytes(bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
package com.mingliqiye.utils.security
|
||||
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.GCMParameterSpec
|
||||
|
||||
@ -9,8 +32,8 @@ const val AES_GCM_NO_PADDING = "AES/GCM/NoPadding"
|
||||
const val AES_GCM_NO_PADDING_IV_LENGTH = 12
|
||||
const val AES_GCM_NO_PADDING_TAG_LENGTH = 16
|
||||
|
||||
fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray,iv: ByteArray): ByteArray {
|
||||
val secretKeySpec = createSecretKeySpec(ALGORITHM,key)
|
||||
fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
|
||||
val secretKeySpec = createSecretKeySpec(ALGORITHM, key)
|
||||
val cipher = Cipher.getInstance(AES_GCM_NO_PADDING)
|
||||
val gcmParameterSpec = GCMParameterSpec(
|
||||
AES_GCM_NO_PADDING_TAG_LENGTH * 8,
|
||||
@ -19,17 +42,13 @@ fun encryptAesGcmNoPadding(src: ByteArray, key: ByteArray,iv: ByteArray): ByteAr
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec)
|
||||
return cipher.doFinal(src)
|
||||
}
|
||||
fun encryptAesGcmNoPadding(src: ByteArray, key: String,iv: ByteArray): ByteArray {
|
||||
|
||||
fun encryptAesGcmNoPadding(src: ByteArray, key: String, iv: ByteArray): ByteArray {
|
||||
return encryptAesGcmNoPadding(src, key.toByteArray(), iv)
|
||||
}
|
||||
fun encryptAesGcmNoPadding(src: String, key: String,iv: ByteArray): ByteArray {
|
||||
|
||||
fun encryptAesGcmNoPadding(src: String, key: String, iv: ByteArray): ByteArray {
|
||||
return encryptAesGcmNoPadding(src.toByteArray(), key.toByteArray(), iv)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val iv = getRandomBytes(16)
|
||||
println(encryptAesGcmNoPadding("mingliqiye","key", iv))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright 2025 mingliqiye
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile SecureUtils.kt
|
||||
* LastUpdate 2025-09-15 22:32:50
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@file:JvmName("SecureUtils")
|
||||
|
||||
package com.mingliqiye.utils.security
|
||||
@ -25,9 +47,9 @@ fun createSecretKey(algorithm: String, data: String): ByteArray {
|
||||
}
|
||||
|
||||
fun createSecretKeySpec(algorithm: String, data: String): SecretKey {
|
||||
return SecretKeySpec( createSecretKey(algorithm,data), algorithm)
|
||||
return SecretKeySpec(createSecretKey(algorithm, data), algorithm)
|
||||
}
|
||||
|
||||
fun createSecretKeySpec(algorithm: String, data: ByteArray): SecretKey {
|
||||
return SecretKeySpec( createSecretKey(algorithm,data), algorithm)
|
||||
}
|
||||
return SecretKeySpec(createSecretKey(algorithm, data), algorithm)
|
||||
}
|
||||
|
||||
@ -16,14 +16,17 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile AutoConfiguration.kt
|
||||
* LastUpdate 2025-09-15 08:51:52
|
||||
* LastUpdate 2025-09-20 10:47:00
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
package com.mingliqiye.utils.springboot.autoconfigure
|
||||
|
||||
import com.mingliqiye.utils.logger.mingLiLoggerFactory
|
||||
import com.mingliqiye.utils.system.getJdkVersion
|
||||
import com.mingliqiye.utils.system.computerName
|
||||
import com.mingliqiye.utils.system.getPid
|
||||
import com.mingliqiye.utils.system.jdkVersion
|
||||
import com.mingliqiye.utils.system.userName
|
||||
import com.mingliqiye.utils.time.DateTime
|
||||
import com.mingliqiye.utils.time.Formatter
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
@ -31,7 +34,7 @@ import java.io.IOException
|
||||
|
||||
@org.springframework.boot.autoconfigure.AutoConfiguration
|
||||
@ComponentScan(
|
||||
"com.mingliqiye.utils.bean.springboot",
|
||||
"com.mingliqiye.utils.springboot.bean",
|
||||
"com.mingliqiye.utils.springboot.converters"
|
||||
)
|
||||
open class AutoConfiguration {
|
||||
@ -51,10 +54,7 @@ open class AutoConfiguration {
|
||||
fun printBanner() {
|
||||
val bannerBuilder = StringBuilder(banner)
|
||||
try {
|
||||
val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data")
|
||||
if (inputStream == null) {
|
||||
return
|
||||
}
|
||||
val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data") ?: return
|
||||
inputStream.use { stream ->
|
||||
var readlen: Int
|
||||
val buffer = ByteArray(1024)
|
||||
@ -63,8 +63,11 @@ open class AutoConfiguration {
|
||||
metaData.append(String(buffer, 0, readlen))
|
||||
}
|
||||
val da = metaData.toString().split("\n").toMutableList()
|
||||
da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7))
|
||||
da.add("jdkRuntime=" + getJdkVersion())
|
||||
da.add("jdkRuntime=$jdkVersion")
|
||||
da.add("pid=$getPid")
|
||||
da.add("computerName=$computerName")
|
||||
da.add("userName=$userName")
|
||||
da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true))
|
||||
da.forEach { s: String ->
|
||||
val d = s.trim { it <= ' ' }.split("=".toRegex(), 2).toTypedArray()
|
||||
if (d.size >= 2) {
|
||||
|
||||
@ -16,11 +16,11 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile SpringBeanUtils.kt
|
||||
* LastUpdate 2025-09-14 22:10:45
|
||||
* LastUpdate 2025-09-19 20:07:08
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
package com.mingliqiye.utils.bean.springboot
|
||||
package com.mingliqiye.utils.springboot.bean
|
||||
|
||||
import org.springframework.beans.BeansException
|
||||
import org.springframework.context.ApplicationContext
|
||||
@ -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
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile StringUtils.kt
|
||||
* LastUpdate 2025-09-14 21:46:14
|
||||
* LastUpdate 2025-09-18 09:26:41
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
@file:JvmName("StringUtils")
|
||||
@ -28,14 +28,17 @@ import com.mingliqiye.utils.logger.mingLiLoggerFactory
|
||||
|
||||
val log = mingLiLoggerFactory.getLogger("StringUtils")
|
||||
|
||||
val NULLISH_STRINGS = setOf("null", "NaN", "undefined", "None", "none")
|
||||
|
||||
/**
|
||||
* 判断`字符串`是否为空
|
||||
*
|
||||
* @param str 待判断的字符串
|
||||
* @return `true`: 空 `false`: 非空
|
||||
*/
|
||||
fun isEmpty(str: String?): Boolean {
|
||||
return str?.isEmpty() != null
|
||||
@JvmName("isEmpty")
|
||||
fun String?.isNullish(): Boolean {
|
||||
return this == null || this.isBlank() || this in NULLISH_STRINGS
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,12 +166,51 @@ fun String.stringBuilder(): java.lang.StringBuilder {
|
||||
return StringBuilder(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将字符串按照指定分隔符进行分割
|
||||
* @param str 需要分割的字符串
|
||||
* @param separator 分割符
|
||||
* @return 分割后的字符串列表
|
||||
*/
|
||||
fun split(str: String, separator: String): List<String> {
|
||||
return str.split(separator)
|
||||
}
|
||||
|
||||
fun List<String>.join(separator: String): String {
|
||||
return this.joinToString(separator)
|
||||
/**
|
||||
* 将列表中的元素使用指定分隔符连接成字符串
|
||||
* @param separator 连接分隔符
|
||||
* @param getstring 转换函数,将列表元素转换为字符串,默认使用toString()方法
|
||||
* @return 连接后的字符串
|
||||
*/
|
||||
fun <T> List<T>.join(separator: String, getstring: (T) -> String = { it.toString() }): String {
|
||||
// 使用StringBuilder构建结果字符串
|
||||
val sb = StringBuilder()
|
||||
for (i in this.indices) {
|
||||
sb.append(this[i])
|
||||
// 除了最后一个元素外,都在后面添加分隔符
|
||||
if (i != this.size - 1) {
|
||||
sb.append(separator)
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用当前字符串作为分隔符,将列表中的元素连接成字符串
|
||||
* @param list 需要连接的元素列表
|
||||
* @param getstring 转换函数,将列表元素转换为字符串,默认使用toString()方法
|
||||
* @return 连接后的字符串
|
||||
*/
|
||||
fun <T> String.join(list: List<T>, getstring: (T) -> String = { it.toString() }): String {
|
||||
// 使用StringBuilder构建结果字符串
|
||||
val sb = StringBuilder()
|
||||
for (i in list.indices) {
|
||||
sb.append(getstring(list[i]))
|
||||
// 除了最后一个元素外,都在后面添加当前字符串作为分隔符
|
||||
if (i != list.size - 1) {
|
||||
sb.append(this)
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
|
||||
@ -16,26 +16,34 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile SystemUtil.kt
|
||||
* LastUpdate 2025-09-15 11:18:34
|
||||
* LastUpdate 2025-09-16 17:36:11
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
@file:JvmName("SystemUtils")
|
||||
|
||||
package com.mingliqiye.utils.system
|
||||
|
||||
import com.mingliqiye.utils.random.randomByteSecure
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.net.Inet4Address
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
|
||||
private val osName: String? = System.getProperties().getProperty("os.name")
|
||||
/**
|
||||
* 操作系统名称属性,延迟初始化
|
||||
*/
|
||||
val osName: String? by lazy {
|
||||
System.getProperties().getProperty("os.name")
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前操作系统是否为Windows系统
|
||||
*
|
||||
* @return 如果是Windows系统返回true,否则返回false
|
||||
*/
|
||||
fun isWindows(): Boolean {
|
||||
return osName != null && osName.startsWith("Windows")
|
||||
val isWindows: Boolean by lazy {
|
||||
osName != null && osName!!.startsWith("Windows")
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,8 +51,8 @@ fun isWindows(): Boolean {
|
||||
*
|
||||
* @return 如果是Mac系统返回true,否则返回false
|
||||
*/
|
||||
fun isMac(): Boolean {
|
||||
return osName != null && osName.startsWith("Mac")
|
||||
val isMac: Boolean by lazy {
|
||||
osName != null && osName!!.startsWith("Mac")
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,31 +60,30 @@ fun isMac(): Boolean {
|
||||
*
|
||||
* @return 如果是Unix/Linux系统返回true,否则返回false
|
||||
*/
|
||||
fun isUnix(): Boolean {
|
||||
val isUnix: Boolean by lazy {
|
||||
if (osName == null) {
|
||||
return false
|
||||
false
|
||||
} else {
|
||||
(osName!!.startsWith("Linux") || osName!!.startsWith("AIX") || osName!!.startsWith("SunOS") || osName!!.startsWith(
|
||||
"Mac OS X"
|
||||
) || osName!!.startsWith(
|
||||
"FreeBSD"
|
||||
))
|
||||
}
|
||||
return (osName.startsWith("Linux") || osName.startsWith("AIX") || osName.startsWith("SunOS") || osName.startsWith("Mac OS X") || osName.startsWith(
|
||||
"FreeBSD"
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JDK版本号
|
||||
*
|
||||
* @return JDK版本号字符串
|
||||
* JDK版本号属性,延迟初始化
|
||||
*/
|
||||
fun getJdkVersion(): String? {
|
||||
return System.getProperty("java.specification.version")
|
||||
val jdkVersion: String? by lazy {
|
||||
System.getProperty("java.specification.version")
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Java版本号的整数形式
|
||||
*
|
||||
* @return Java版本号的整数形式(如:8、11、17等)
|
||||
* Java版本号的整数形式属性,延迟初始化
|
||||
*/
|
||||
fun getJavaVersionAsInteger(): Int {
|
||||
val version = getJdkVersion()
|
||||
val javaVersionAsInteger: Int by lazy {
|
||||
val version = jdkVersion
|
||||
if (version == null || version.isEmpty()) {
|
||||
throw IllegalStateException(
|
||||
"Unable to determine Java version from property 'java.specification.version'"
|
||||
@ -98,7 +105,7 @@ fun getJavaVersionAsInteger(): Int {
|
||||
}
|
||||
version.take(2)
|
||||
}
|
||||
return uversion.toInt()
|
||||
uversion.toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,18 +113,17 @@ fun getJavaVersionAsInteger(): Int {
|
||||
*
|
||||
* @return 如果JDK版本大于8返回true,否则返回false
|
||||
*/
|
||||
fun isJdk8Plus(): Boolean {
|
||||
return getJavaVersionAsInteger() > 8
|
||||
val isJdk8Plus: Boolean by lazy {
|
||||
javaVersionAsInteger > 8
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地IP地址数组
|
||||
* 本地IP地址数组,延迟初始化
|
||||
*
|
||||
* @return 本地IP地址字符串数组
|
||||
* @throws RuntimeException 当获取网络接口信息失败时抛出
|
||||
*/
|
||||
fun getLocalIps(): Array<String> {
|
||||
return try {
|
||||
val localIps: Array<String> by lazy {
|
||||
try {
|
||||
val ipList: MutableList<String> = ArrayList()
|
||||
val interfaces = NetworkInterface.getNetworkInterfaces()
|
||||
|
||||
@ -145,22 +151,18 @@ fun getLocalIps(): Array<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地IP地址列表
|
||||
*
|
||||
* @return 本地IP地址的字符串列表
|
||||
* 本地IP地址列表,延迟初始化
|
||||
*/
|
||||
fun getLocalIpsByList(): List<String> {
|
||||
return getLocalIps().toList()
|
||||
val localIpsByList: List<String> by lazy {
|
||||
localIps.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地回环地址
|
||||
*
|
||||
* @return 回环地址字符串,通常为"127.0.0.1"
|
||||
* 本地回环地址数组,延迟初始化
|
||||
*/
|
||||
fun getLoopbackIps(): Array<String> {
|
||||
val loopbackIps: Array<String> by lazy {
|
||||
val strings: MutableList<String> = ArrayList(3)
|
||||
return try {
|
||||
return@lazy try {
|
||||
val interfaces = NetworkInterface.getNetworkInterfaces()
|
||||
|
||||
while (interfaces.hasMoreElements()) {
|
||||
@ -183,11 +185,193 @@ fun getLoopbackIps(): Array<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地回环地址IP列表
|
||||
*
|
||||
* @return 本地回环地址IP字符串列表的副本
|
||||
* 本地回环地址IP列表,延迟初始化
|
||||
*/
|
||||
fun getLoopbackIpsByList(): List<String> {
|
||||
// 将本地回环地址IP数组转换为列表并返回
|
||||
return getLoopbackIps().toList()
|
||||
val loopbackIpsByList: List<String> by lazy {
|
||||
loopbackIps.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前进程的PID
|
||||
*
|
||||
* @return 进程ID,如果无法获取则返回-1
|
||||
*/
|
||||
val getPid: Long by lazy {
|
||||
try {
|
||||
val name = ManagementFactory.getRuntimeMXBean().name
|
||||
val index = name.indexOf('@')
|
||||
if (index > 0) {
|
||||
name.take(index).toLong()
|
||||
} else {
|
||||
-1L
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
-1L
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前进程的PID字符串形式
|
||||
*
|
||||
* @return 进程ID字符串,如果无法获取则返回"-1"
|
||||
*/
|
||||
val pidAsString: String by lazy {
|
||||
try {
|
||||
val name = ManagementFactory.getRuntimeMXBean().name
|
||||
val index = name.indexOf('@')
|
||||
if (index > 0) {
|
||||
name.take(index)
|
||||
} else {
|
||||
"-1"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
"-1"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取计算机名
|
||||
*
|
||||
* @return 计算机名,如果无法获取则返回"unknown"
|
||||
*/
|
||||
val computerName: String by lazy {
|
||||
try {
|
||||
var name = System.getenv("COMPUTERNAME")
|
||||
if (name.isNullOrBlank()) {
|
||||
name = System.getenv("HOSTNAME")
|
||||
}
|
||||
if (name.isNullOrBlank()) {
|
||||
name = InetAddress.getLocalHost().hostName
|
||||
}
|
||||
name ?: "unknown"
|
||||
} catch (e: Exception) {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户名
|
||||
*
|
||||
* @return 当前用户名,如果无法获取则返回"unknown"
|
||||
*/
|
||||
val userName: String by lazy {
|
||||
try {
|
||||
getEnvVar("USERNAME")
|
||||
?: getEnvVar("USER")
|
||||
?: System.getProperty("user.name")
|
||||
?: "unknown"
|
||||
} catch (e: SecurityException) {
|
||||
"unknown"
|
||||
} catch (e: Exception) {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEnvVar(name: String): String? {
|
||||
return try {
|
||||
System.getenv(name)?.takeIf { it.isNotBlank() }
|
||||
} catch (e: SecurityException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本机MAC地址的字节数组形式
|
||||
*
|
||||
* @return MAC地址字节数组,如果无法获取则返回空数组
|
||||
*/
|
||||
val macAddressBytes: ByteArray by lazy {
|
||||
try {
|
||||
val interfaces = NetworkInterface.getNetworkInterfaces()
|
||||
|
||||
while (interfaces.hasMoreElements()) {
|
||||
val networkInterface = interfaces.nextElement()
|
||||
|
||||
// 跳过回环接口和虚拟接口
|
||||
if (networkInterface.isLoopback || networkInterface.isVirtual || !networkInterface.isUp) {
|
||||
continue
|
||||
}
|
||||
|
||||
val mac = networkInterface.hardwareAddress
|
||||
if (mac != null && mac.isNotEmpty()) {
|
||||
return@lazy mac
|
||||
}
|
||||
}
|
||||
randomByteSecure(6)
|
||||
} catch (e: SocketException) {
|
||||
randomByteSecure(6)
|
||||
} catch (e: Exception) {
|
||||
randomByteSecure(6)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本机MAC地址的十六进制字符串列表形式
|
||||
*
|
||||
* @return MAC地址字符串列表,每个元素表示一个字节的十六进制值(大写),如果无法获取则返回空列表
|
||||
*/
|
||||
val macAddressStringList: List<String> by lazy {
|
||||
val macBytes = macAddressBytes
|
||||
if (macBytes.isEmpty()) {
|
||||
return@lazy emptyList()
|
||||
}
|
||||
|
||||
macBytes.map { String.format("%02X", it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本机MAC地址的格式化字符串形式(如 "00:11:22:33:44:55")
|
||||
*
|
||||
* @return 格式化的MAC地址字符串,如果无法获取则返回空字符串
|
||||
*/
|
||||
val macAddressFormattedString: String by lazy {
|
||||
val macBytes = macAddressBytes
|
||||
if (macBytes.isEmpty()) {
|
||||
return@lazy ""
|
||||
}
|
||||
|
||||
macBytes.joinToString(":") { String.format("%02X", it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有网络接口的MAC地址映射
|
||||
*
|
||||
* @return Map结构,key为网络接口名称,value为对应的MAC地址字节数组
|
||||
*/
|
||||
val allMacAddresses: Map<String, ByteArray> by lazy {
|
||||
try {
|
||||
val result = mutableMapOf<String, ByteArray>()
|
||||
val interfaces = NetworkInterface.getNetworkInterfaces()
|
||||
|
||||
while (interfaces.hasMoreElements()) {
|
||||
val networkInterface = interfaces.nextElement()
|
||||
|
||||
// 跳过回环接口和虚拟接口
|
||||
if (networkInterface.isLoopback || networkInterface.isVirtual) {
|
||||
continue
|
||||
}
|
||||
|
||||
val mac = networkInterface.hardwareAddress
|
||||
if (mac != null && mac.isNotEmpty()) {
|
||||
result[networkInterface.name] = mac
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
} catch (e: SocketException) {
|
||||
emptyMap()
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有网络接口的MAC地址字符串列表映射
|
||||
*
|
||||
* @return Map结构,key为网络接口名称,value为对应的MAC地址字符串列表
|
||||
*/
|
||||
val allMacAddressesStringList: Map<String, List<String>> by lazy {
|
||||
allMacAddresses.mapValues { entry ->
|
||||
entry.value.map { String.format("%02X", it) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile DateTime.kt
|
||||
* LastUpdate 2025-09-15 09:57:50
|
||||
* LastUpdate 2025-09-17 19:06:39
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
@ -27,8 +27,8 @@ import com.mingliqiye.utils.jna.NANOS_PER_100NS
|
||||
import com.mingliqiye.utils.jna.WinKernel32Api
|
||||
import com.mingliqiye.utils.jna.getWinKernel32Apis
|
||||
import com.mingliqiye.utils.logger.mingLiLoggerFactory
|
||||
import com.mingliqiye.utils.system.getJavaVersionAsInteger
|
||||
import com.mingliqiye.utils.system.isWindows
|
||||
import com.mingliqiye.utils.system.javaVersionAsInteger
|
||||
import org.slf4j.Logger
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDateTime
|
||||
@ -46,8 +46,7 @@ import kotlin.time.Instant
|
||||
* @author MingLiPro
|
||||
*/
|
||||
class DateTimeOffset private constructor(
|
||||
val offsetType: ChronoUnit,
|
||||
val offset: Long
|
||||
val offsetType: ChronoUnit, val offset: Long
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@ -96,6 +95,8 @@ enum class Formatter(private val value: String) {
|
||||
* 标准日期时间格式(7位毫秒):yyyy-MM-dd HH:mm:ss.SSSSSSS
|
||||
*/
|
||||
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
|
||||
STANDARD_DATETIME_MILLISECOUND8("yyyy-MM-dd HH:mm:ss.SSSSSSSS"),
|
||||
STANDARD_DATETIME_MILLISECOUND9("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"),
|
||||
|
||||
/**
|
||||
* 标准日期时间格式(6位毫秒):yyyy-MM-dd HH:mm:ss.SSSSSS
|
||||
@ -152,6 +153,7 @@ enum class Formatter(private val value: String) {
|
||||
*/
|
||||
COMPACT_DATETIME("yyyyMMddHHmmss");
|
||||
|
||||
|
||||
private val len: Int = value.length
|
||||
|
||||
fun getLen(): Int {
|
||||
@ -179,15 +181,11 @@ enum class Formatter(private val value: String) {
|
||||
* @see Instant
|
||||
*/
|
||||
class DateTime private constructor(
|
||||
private var localDateTime: LocalDateTime,
|
||||
private val zoneId: ZoneId = ZoneId.systemDefault()
|
||||
private var localDateTime: LocalDateTime, private val zoneId: ZoneId = ZoneId.systemDefault()
|
||||
) : Serializable {
|
||||
|
||||
companion object {
|
||||
private val WIN_KERNEL_32_API: WinKernel32Api? = if (
|
||||
getJavaVersionAsInteger() == 8 &&
|
||||
isWindows()
|
||||
) {
|
||||
private val WIN_KERNEL_32_API: WinKernel32Api? = if (javaVersionAsInteger == 8 && isWindows) {
|
||||
val log: Logger = mingLiLoggerFactory.getLogger("mingli-utils DateTime")
|
||||
val a = getWinKernel32Apis()
|
||||
|
||||
@ -219,9 +217,7 @@ class DateTime private constructor(
|
||||
fun now(): DateTime {
|
||||
if (WIN_KERNEL_32_API != null) {
|
||||
return DateTime(
|
||||
WIN_KERNEL_32_API.getTime()
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime()
|
||||
WIN_KERNEL_32_API.getTime().atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||||
)
|
||||
}
|
||||
return DateTime(LocalDateTime.now())
|
||||
@ -273,9 +269,7 @@ class DateTime private constructor(
|
||||
*/
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
timestr: String,
|
||||
formatter: String,
|
||||
fillZero: Boolean
|
||||
timestr: String, formatter: String, fillZero: Boolean
|
||||
): DateTime {
|
||||
return DateTime(
|
||||
LocalDateTime.parse(
|
||||
@ -285,6 +279,20 @@ class DateTime private constructor(
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
timestr: String
|
||||
): DateTime {
|
||||
|
||||
val formatterString = Formatter.STANDARD_DATETIME_MILLISECOUND9.getValue()
|
||||
return DateTime(
|
||||
LocalDateTime.parse(
|
||||
getFillZeroByLen(timestr, formatterString),
|
||||
DateTimeFormatter.ofPattern(formatterString)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例。
|
||||
*
|
||||
@ -295,9 +303,7 @@ class DateTime private constructor(
|
||||
*/
|
||||
@JvmStatic
|
||||
fun parse(
|
||||
timestr: String,
|
||||
formatter: Formatter,
|
||||
fillZero: Boolean
|
||||
timestr: String, formatter: Formatter, fillZero: Boolean
|
||||
): DateTime {
|
||||
return parse(timestr, formatter.getValue(), fillZero)
|
||||
}
|
||||
@ -343,18 +349,14 @@ class DateTime private constructor(
|
||||
modifiedDstr += "."
|
||||
}
|
||||
val sb = StringBuilder(modifiedDstr)
|
||||
for (i in 0 until formats.length - dstr.length) {
|
||||
for (i in 0 until formats.length - sb.length) {
|
||||
sb.append("0")
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
throw IllegalArgumentException(
|
||||
String.format(
|
||||
"Text: '%s' len %s < %s %s",
|
||||
dstr,
|
||||
dstr.length,
|
||||
formats,
|
||||
formats.length
|
||||
"Text: '%s' len %s < %s %s", dstr, dstr.length, formats, formats.length
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -384,11 +386,7 @@ class DateTime private constructor(
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(
|
||||
year: Int,
|
||||
month: Int,
|
||||
day: Int,
|
||||
hour: Int,
|
||||
minute: Int
|
||||
year: Int, month: Int, day: Int, hour: Int, minute: Int
|
||||
): DateTime {
|
||||
return DateTime(LocalDateTime.of(year, month, day, hour, minute))
|
||||
}
|
||||
@ -407,8 +405,7 @@ class DateTime private constructor(
|
||||
|
||||
// 2. 从纳秒时间戳创建 Instant
|
||||
val instant = java.time.Instant.ofEpochSecond(
|
||||
unixNanos / 1_000_000_000L,
|
||||
unixNanos % 1_000_000_000L
|
||||
unixNanos / 1_000_000_000L, unixNanos % 1_000_000_000L
|
||||
)
|
||||
|
||||
// 3. 转换为系统默认时区的 LocalDateTime
|
||||
@ -428,12 +425,7 @@ class DateTime private constructor(
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(
|
||||
year: Int,
|
||||
month: Int,
|
||||
day: Int,
|
||||
hour: Int,
|
||||
minute: Int,
|
||||
second: Int
|
||||
year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int
|
||||
): DateTime {
|
||||
return DateTime(
|
||||
LocalDateTime.of(year, month, day, hour, minute, second)
|
||||
@ -454,13 +446,7 @@ class DateTime private constructor(
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(
|
||||
year: Int,
|
||||
month: Int,
|
||||
day: Int,
|
||||
hour: Int,
|
||||
minute: Int,
|
||||
second: Int,
|
||||
nano: Int
|
||||
year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, nano: Int
|
||||
): DateTime {
|
||||
return DateTime(
|
||||
LocalDateTime.of(year, month, day, hour, minute, second, nano)
|
||||
@ -476,9 +462,14 @@ class DateTime private constructor(
|
||||
@JvmStatic
|
||||
fun of(epochMilli: Long): DateTime {
|
||||
return DateTime(
|
||||
java.time.Instant.ofEpochMilli(epochMilli)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime()
|
||||
java.time.Instant.ofEpochMilli(epochMilli).atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun of(seconds: Long, nanos: Long): DateTime {
|
||||
return DateTime(
|
||||
java.time.Instant.ofEpochSecond(seconds, nanos).atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||||
)
|
||||
}
|
||||
|
||||
@ -492,8 +483,7 @@ class DateTime private constructor(
|
||||
@JvmStatic
|
||||
fun of(epochMilli: Long, zoneId: ZoneId): DateTime {
|
||||
return DateTime(
|
||||
java.time.Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime(),
|
||||
zoneId
|
||||
java.time.Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime(), zoneId
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -525,8 +515,7 @@ class DateTime private constructor(
|
||||
fun add(dateTimeOffset: DateTimeOffset): DateTime {
|
||||
return DateTime(
|
||||
this.localDateTime.plus(
|
||||
dateTimeOffset.offset,
|
||||
dateTimeOffset.offsetType
|
||||
dateTimeOffset.offset, dateTimeOffset.offsetType
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -538,12 +527,7 @@ class DateTime private constructor(
|
||||
* @return 返回修改后的 DateTime 实例
|
||||
*/
|
||||
fun sub(dateTimeOffset: DateTimeOffset): DateTime {
|
||||
return DateTime(
|
||||
this.localDateTime.plus(
|
||||
-dateTimeOffset.offset,
|
||||
dateTimeOffset.offsetType
|
||||
)
|
||||
)
|
||||
return add(DateTimeOffset.of(-dateTimeOffset.offset, dateTimeOffset.offsetType))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -566,6 +550,10 @@ class DateTime private constructor(
|
||||
return format(formatter.getValue())
|
||||
}
|
||||
|
||||
fun format(): String {
|
||||
return format(Formatter.STANDARD_DATETIME_MILLISECOUND9.getValue(), true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定格式化模板将当前时间格式化为字符串,并可选择是否去除末尾多余的零。
|
||||
*
|
||||
@ -602,10 +590,7 @@ class DateTime private constructor(
|
||||
* @return 返回标准格式的时间字符串
|
||||
*/
|
||||
override fun toString(): String {
|
||||
return String.format(
|
||||
"DateTime(%s)",
|
||||
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
|
||||
)
|
||||
return "DateTime(${format()})"
|
||||
}
|
||||
|
||||
/**
|
||||
@ -675,4 +660,34 @@ class DateTime private constructor(
|
||||
fun getZoneId(): ZoneId {
|
||||
return zoneId
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将 Instant 转换为纳秒时间戳
|
||||
* @throws ArithmeticException 如果结果超出 Long 范围 (-2^63 到 2^63-1)
|
||||
*/
|
||||
fun toNanoTime(): Long {
|
||||
val instant = toInstant()
|
||||
|
||||
return try {
|
||||
val secondsInNanos = Math.multiplyExact(instant.epochSecond, 1_000_000_000L)
|
||||
Math.addExact(secondsInNanos, instant.nano.toLong())
|
||||
} catch (e: ArithmeticException) {
|
||||
throw ArithmeticException(
|
||||
"无法将 Instant(${instant.epochSecond}s, ${instant.nano}ns) 转换为纳秒: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun to100NanoTime(): Long {
|
||||
return toInstant().let {
|
||||
(it.epochSecond * 10_000_000L) + (it.nano / 100L)
|
||||
}
|
||||
}
|
||||
|
||||
fun toMillisecondTime(): Long {
|
||||
return toInstant().let {
|
||||
(it.epochSecond * 1000L) + (it.nano / 1_000_000L)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile MysqlUUIDv1.kt
|
||||
* LastUpdate 2025-09-14 18:19:29
|
||||
* LastUpdate 2025-09-15 22:32:50
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
@file:JvmName("MysqlUUIDv1")
|
||||
|
||||
@ -16,70 +16,195 @@
|
||||
* ProjectName mingli-utils
|
||||
* ModuleName mingli-utils.main
|
||||
* CurrentFile UUID.kt
|
||||
* LastUpdate 2025-09-14 22:38:51
|
||||
* LastUpdate 2025-09-19 20:22:27
|
||||
* UpdateUser MingLiPro
|
||||
*/
|
||||
|
||||
package com.mingliqiye.utils.uuid
|
||||
|
||||
import com.github.f4b6a3.uuid.UuidCreator
|
||||
import com.mingliqiye.utils.base.BASE256
|
||||
import com.mingliqiye.utils.base.BASE64
|
||||
import com.mingliqiye.utils.base.BASE91
|
||||
import com.mingliqiye.utils.random.randomByteSecure
|
||||
import com.mingliqiye.utils.random.secureRandom
|
||||
import com.mingliqiye.utils.system.macAddressBytes
|
||||
import com.mingliqiye.utils.time.DateTime
|
||||
import com.mingliqiye.utils.time.DateTimeOffset
|
||||
import java.io.Serializable
|
||||
import java.nio.ByteBuffer
|
||||
import java.security.MessageDigest
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import java.util.UUID as JUUID
|
||||
|
||||
|
||||
/**
|
||||
* UUID 类用于生成和操作不同版本的 UUID(通用唯一标识符)。
|
||||
* 支持 UUID 的序列化、转换、解析和时间/版本信息提取。
|
||||
*/
|
||||
class UUID : Serializable {
|
||||
private val uuid: JUUID
|
||||
private val data: ByteArray
|
||||
private val mostSigBits: Long
|
||||
private val leastSigBits: Long
|
||||
private val version: Int
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 获取 UUIDV1 版本的随机实例
|
||||
* @return UUID
|
||||
* 预期 UUID 字符串中连字符的位置数组。
|
||||
*/
|
||||
@JvmStatic
|
||||
val expectedHyphenPositions = intArrayOf(8, 13, 18, 23)
|
||||
|
||||
/**
|
||||
* UUID 纪元偏移量(以天为单位)。
|
||||
*/
|
||||
@JvmStatic
|
||||
val UUID_EPOCH_OFFSET = 141427L
|
||||
|
||||
|
||||
@JvmStatic
|
||||
fun ofBase64ShortString(baseShortString: String): UUID {
|
||||
return UUID(BASE64.decode(baseShortString))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun ofBase256ShortString(baseShortString: String): UUID {
|
||||
return UUID(BASE256.decode(baseShortString))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun ofBase91ShortString(baseShortString: String): UUID {
|
||||
return UUID(BASE91.decode(baseShortString))
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个 UUID V1 版本,使用系统时钟、随机数和 MAC 地址。
|
||||
* 如果 MAC 地址为空,则使用随机生成的 MAC 地址。
|
||||
*
|
||||
* @return UUID V1 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getV1(): UUID {
|
||||
return UUID(UuidCreator.getTimeBased())
|
||||
val time = DateTime.now().add(DateTimeOffset.of(UUID_EPOCH_OFFSET, ChronoUnit.DAYS)).to100NanoTime()
|
||||
|
||||
val timeLow = (time and 0xFFFFFFFFL).toInt()
|
||||
val timeMid = ((time shr 32) and 0xFFFFL).toShort()
|
||||
val timeHighAndVersion = (((time shr 48) and 0x0FFFL) or 0x1000L).toShort()
|
||||
val clockSeq = (secureRandom.nextInt(16384)) and 0x3FFF
|
||||
|
||||
val byteBuffer = ByteBuffer.wrap(ByteArray(16))
|
||||
byteBuffer.putInt(timeLow)
|
||||
byteBuffer.putShort(timeMid)
|
||||
byteBuffer.putShort(timeHighAndVersion)
|
||||
byteBuffer.putShort(clockSeq.toShort())
|
||||
byteBuffer.put(macAddressBytes)
|
||||
|
||||
return UUID(byteBuffer.array())
|
||||
}
|
||||
|
||||
@Deprecated("使用 getV1()", ReplaceWith("getV1()"), level = DeprecationLevel.WARNING)
|
||||
fun getTimeBased(): UUID = getV1()
|
||||
|
||||
/**
|
||||
* 获取 UUIDV4 版本的随机实例
|
||||
* @return UUID
|
||||
* 生成一个 UUID V4 版本,使用加密安全的随机数。
|
||||
*
|
||||
* @return UUID V4 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getV4(): UUID {
|
||||
return UUID(UuidCreator.getRandomBased())
|
||||
val randomBytes = randomByteSecure(16)
|
||||
randomBytes[6] = (randomBytes[6].toInt() and 0x0F).toByte()
|
||||
randomBytes[6] = (randomBytes[6].toInt() or 0x40).toByte()
|
||||
randomBytes[8] = (randomBytes[8].toInt() and 0x3F).toByte()
|
||||
randomBytes[8] = (randomBytes[8].toInt() or 0x80).toByte()
|
||||
return UUID(randomBytes)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 UUIDV1Fast 版本的随机实例
|
||||
* @return UUID
|
||||
* 生成一个 UUID V3 版本,基于命名空间和名称的 MD5 哈希值。
|
||||
*
|
||||
* @param namepath 命名空间 UUID
|
||||
* @param user 用户提供的字符串
|
||||
* @return UUID V3 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getV4Fast(): UUID {
|
||||
return UUID(UuidCreator.getRandomBasedFast())
|
||||
fun getV3(namepath: UUID, user: String): UUID {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
val userB = user.toByteArray()
|
||||
val array = md.digest(
|
||||
ByteBuffer.wrap(ByteArray(16 + userB.size)).put(namepath.data).put(userB).array()
|
||||
)
|
||||
array[6] = (array[6].toInt() and 0x0F or 0x30).toByte()
|
||||
array[8] = (array[8].toInt() and 0x3F or 0x80).toByte()
|
||||
return UUID(array)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从2个8个字节转换到UUID
|
||||
* @param lsb 高位 8 字节的 Long
|
||||
* @param msb 低位 8 字节的 Long
|
||||
* @return UUID
|
||||
* 生成一个 UUID V5 版本,基于命名空间和名称的 SHA-1 哈希值。
|
||||
*
|
||||
* @param namepath 命名空间 UUID
|
||||
* @param user 用户提供的字符串
|
||||
* @return UUID V5 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(msb: Long, lsb: Long): UUID {
|
||||
return UUID(msb, lsb)
|
||||
fun getV5(namepath: UUID, user: String): UUID {
|
||||
val sha1 = MessageDigest.getInstance("SHA-1")
|
||||
val userB = user.toByteArray()
|
||||
val array = sha1.digest(
|
||||
ByteBuffer.wrap(ByteArray(namepath.data.size + userB.size)).put(namepath.data).put(userB).array()
|
||||
)
|
||||
array[6] = (array[6].toInt() and 0x0F or 0x50).toByte()
|
||||
array[8] = (array[8].toInt() and 0x3F or 0x80).toByte()
|
||||
return UUID(ByteBuffer.wrap(ByteArray(16)).put(array, 0, 16).array())
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串格式化
|
||||
* @param uuid 字符串
|
||||
* @return UUID
|
||||
* 生成一个 UUID V6 版本,使用时间戳和随机节点信息。
|
||||
*
|
||||
* @return UUID V6 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getV6(): UUID {
|
||||
val timestamp = DateTime.now()
|
||||
.add(DateTimeOffset.of(UUID_EPOCH_OFFSET, ChronoUnit.DAYS))
|
||||
.to100NanoTime() and 0x0FFFFFFFFFFFFFFFL
|
||||
val timeHigh = (timestamp ushr 12) and 0xFFFFFFFFFFFFL
|
||||
val timeMid = timestamp and 0x0FFFL
|
||||
val clockSeq = secureRandom.nextInt(16384) and 0x3FFF
|
||||
val node = secureRandom.nextLong() and 0x0000FFFFFFFFFFFFL
|
||||
val buffer = ByteBuffer.allocate(16)
|
||||
buffer.putLong((timeHigh shl 16) or (0x6000L) or (timeMid and 0x0FFF))
|
||||
buffer.putShort((0x8000 or (clockSeq and 0x3FFF)).toShort())
|
||||
buffer.put((node shr 40).toByte())
|
||||
buffer.put((node shr 32).toByte())
|
||||
buffer.put((node shr 24).toByte())
|
||||
buffer.put((node shr 16).toByte())
|
||||
buffer.put((node shr 8).toByte())
|
||||
buffer.put(node.toByte())
|
||||
return UUID(buffer.array())
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个 UUID V7 版本,使用毫秒级时间戳和随机数。
|
||||
*
|
||||
* @return UUID V7 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getV7(): UUID {
|
||||
val instant = DateTime.now().toMillisecondTime()
|
||||
val buffer = ByteBuffer.allocate(16)
|
||||
buffer.putInt((instant shr 16).toInt())
|
||||
buffer.putShort((instant).toShort())
|
||||
buffer.put(randomByteSecure(2))
|
||||
buffer.putLong(secureRandom.nextLong())
|
||||
val bytes = buffer.array()
|
||||
bytes[6] = (bytes[6].toInt() and 0x0F or 0x70).toByte()
|
||||
bytes[8] = (bytes[8].toInt() and 0x3F or 0x80).toByte()
|
||||
return UUID(bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字符串创建 UUID 实例。
|
||||
*
|
||||
* @param uuid UUID 字符串
|
||||
* @return UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(uuid: String): UUID {
|
||||
@ -87,135 +212,470 @@ class UUID : Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Java的UUID
|
||||
* @param uuid 字符串
|
||||
* @return UUID
|
||||
* 根据字节数组创建 UUID 实例。
|
||||
*
|
||||
* @param uuid UUID 字节数组
|
||||
* @return UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(uuid: JUUID): UUID {
|
||||
fun of(uuid: ByteArray): UUID {
|
||||
return UUID(uuid)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字节码转换到UUID
|
||||
* @param array 16字节
|
||||
* @return UUID
|
||||
* 根据高位和低位长整型创建 UUID 实例。
|
||||
*
|
||||
* @param msb 高位长整型
|
||||
* @param lsb 低位长整型
|
||||
* @return UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(array: ByteArray): UUID {
|
||||
return UUID(array)
|
||||
fun of(msb: Long, lsb: Long): UUID {
|
||||
return UUID(msb, lsb)
|
||||
}
|
||||
|
||||
fun JUUID.toMlUUID(): UUID {
|
||||
return of(this)
|
||||
/**
|
||||
* 根据 MySQL UUID 字节数组创建 UUID 实例。
|
||||
*
|
||||
* @param byteArray MySQL UUID 字节数组
|
||||
* @return UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun ofMysqlUUID(byteArray: ByteArray): UUID {
|
||||
return UUID(mysqlToUuid(byteArray))
|
||||
}
|
||||
}
|
||||
|
||||
internal constructor(msb: Long, lsb: Long) {
|
||||
uuid = JUUID(msb, lsb)
|
||||
}
|
||||
/**
|
||||
* 根据 MySQL UUID 实例创建 UUID 实例。
|
||||
*
|
||||
* @param uuid MySQL UUID 实例
|
||||
* @return UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun ofMysqlUUID(uuid: UUID): UUID {
|
||||
return UUID(mysqlToUuid(uuid.data))
|
||||
}
|
||||
|
||||
internal constructor(uuid: JUUID) {
|
||||
this.uuid = uuid
|
||||
}
|
||||
/**
|
||||
* 获取最大 UUID(所有字节为 0xFF)。
|
||||
*
|
||||
* @return 最大 UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getMaxUUID(): UUID {
|
||||
return UUID(
|
||||
ByteArray(
|
||||
16
|
||||
) { 0xFF.toByte() }
|
||||
)
|
||||
}
|
||||
|
||||
internal constructor(array: ByteArray) {
|
||||
val bb = ByteBuffer.wrap(array)
|
||||
this.uuid = JUUID(bb.getLong(), bb.getLong())
|
||||
}
|
||||
/**
|
||||
* 获取最小 UUID(所有字节为 0x00)。
|
||||
*
|
||||
* @return 最小 UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getMinUUID(): UUID {
|
||||
return UUID(
|
||||
ByteArray(
|
||||
16
|
||||
) { 0x00.toByte() }
|
||||
)
|
||||
}
|
||||
|
||||
constructor(uuid: String) {
|
||||
this.uuid = JUUID.fromString(uuid)
|
||||
}
|
||||
/**
|
||||
* 将 Java UUID 转换为自定义 UUID 实例。
|
||||
*
|
||||
* @receiver Java UUID 实例
|
||||
* @return 自定义 UUID 实例
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("ofJUUID")
|
||||
fun JUUID.toMLUUID(): UUID {
|
||||
return UUID(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从字符串解析 UUID 字节数组。
|
||||
*
|
||||
* @param uuidString UUID 字符串
|
||||
* @return UUID 字节数组
|
||||
*/
|
||||
private fun fromString(uuidString: String): ByteArray {
|
||||
val cleanStr = when (uuidString.length) {
|
||||
36 -> {
|
||||
for (i in expectedHyphenPositions) {
|
||||
if (uuidString[i] != '-') {
|
||||
throw IllegalArgumentException("Invalid UUID string: $uuidString at index $i")
|
||||
}
|
||||
}
|
||||
uuidString.replace("-", "")
|
||||
}
|
||||
|
||||
32 -> uuidString
|
||||
else -> throw IllegalArgumentException("Invalid UUID string: $uuidString")
|
||||
}
|
||||
|
||||
// 直接按索引提取各段并校验
|
||||
val segments = arrayOf(
|
||||
cleanStr.take(8),
|
||||
cleanStr.substring(8, 12),
|
||||
cleanStr.substring(12, 16),
|
||||
cleanStr.substring(16, 20),
|
||||
cleanStr.substring(20, 32)
|
||||
)
|
||||
|
||||
for (segment in segments) {
|
||||
if (!segment.matches(Regex("^[0-9A-Fa-f]+$"))) {
|
||||
throw IllegalArgumentException("Invalid UUID segment: $segment")
|
||||
}
|
||||
}
|
||||
|
||||
return cleanStr.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 MAC 地址字节数组转换为长整型。
|
||||
*
|
||||
* @param mac MAC 地址字节数组(必须为 6 字节)
|
||||
* @return MAC 地址对应的长整型值
|
||||
*/
|
||||
private fun macBytesToLong(mac: ByteArray): Long {
|
||||
require(mac.size == 6) { "MAC地址必须是6字节" }
|
||||
var result = 0L
|
||||
for (i in 0 until 6) {
|
||||
result = (result shl 8) or (mac[i].toLong() and 0xFF)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应的字节码
|
||||
* @return 字节码
|
||||
* 构造函数,根据字节数组初始化 UUID。
|
||||
*
|
||||
* @param data UUID 字节数组(必须为 16 字节)
|
||||
*/
|
||||
constructor(data: ByteArray) {
|
||||
require(data.size == 16) { "UUID byte array length must be 16" }
|
||||
this.data = data
|
||||
val bb: ByteBuffer = ByteBuffer.wrap(data)
|
||||
mostSigBits = bb.long
|
||||
leastSigBits = bb.long
|
||||
version = (mostSigBits shr 12 and 0xF).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数,根据高位和低位长整型初始化 UUID。
|
||||
*
|
||||
* @param msb 高位长整型
|
||||
* @param lsb 低位长整型
|
||||
*/
|
||||
constructor(msb: Long, lsb: Long) {
|
||||
mostSigBits = msb
|
||||
leastSigBits = lsb
|
||||
val bb: ByteBuffer = ByteBuffer.wrap(ByteArray(16))
|
||||
bb.putLong(msb)
|
||||
bb.putLong(lsb)
|
||||
this.data = bb.array()
|
||||
version = (mostSigBits shr 12 and 0xF).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数,根据 Java UUID 初始化自定义 UUID。
|
||||
*
|
||||
* @param juuid Java UUID 实例
|
||||
*/
|
||||
constructor(juuid: JUUID) {
|
||||
mostSigBits = juuid.mostSignificantBits
|
||||
leastSigBits = juuid.leastSignificantBits
|
||||
val bb: ByteBuffer = ByteBuffer.wrap(ByteArray(16))
|
||||
bb.putLong(mostSigBits)
|
||||
bb.putLong(leastSigBits)
|
||||
this.data = bb.array()
|
||||
version = (mostSigBits shr 12 and 0xF).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数,根据字符串初始化 UUID。
|
||||
*
|
||||
* @param uuid UUID 字符串
|
||||
*/
|
||||
constructor(uuid: String) {
|
||||
fromString(uuid).let {
|
||||
val bb: ByteBuffer = ByteBuffer.wrap(it)
|
||||
mostSigBits = bb.long
|
||||
leastSigBits = bb.long
|
||||
this.data = it
|
||||
version = (mostSigBits shr 12 and 0xF).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 UUID 的字节数组表示。
|
||||
*
|
||||
* @return UUID 字节数组
|
||||
*/
|
||||
fun toBytes(): ByteArray {
|
||||
val bb = ByteBuffer.wrap(ByteArray(16))
|
||||
bb.putLong(uuid.mostSignificantBits)
|
||||
bb.putLong(uuid.leastSignificantBits)
|
||||
|
||||
return bb.array()
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Java的UUID对象
|
||||
* @return Java的UUID对象
|
||||
* 返回 UUID 的高位长整型部分。
|
||||
*
|
||||
* @return 高位长整型
|
||||
*/
|
||||
fun getMostSignificantBits(): Long {
|
||||
return mostSigBits
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 UUID 的低位长整型部分。
|
||||
*
|
||||
* @return 低位长整型
|
||||
*/
|
||||
fun getLeastSignificantBits(): Long {
|
||||
return leastSigBits
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 UUID 的字符串表示。
|
||||
*
|
||||
* @param isUpper 是否使用大写字母(默认为 false)
|
||||
* @return UUID 字符串
|
||||
*/
|
||||
fun getString(isUpper: Boolean = false): String {
|
||||
return getString().let {
|
||||
if (isUpper) {
|
||||
it.uppercase()
|
||||
} else it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 UUID 的字符串表示。
|
||||
*
|
||||
* @param isUpper 是否使用大写字母(默认为 false)
|
||||
* @param isnotSpace 是否移除连字符(默认为 false)
|
||||
* @return UUID 字符串
|
||||
*/
|
||||
fun getString(isUpper: Boolean = false, isnotSpace: Boolean = false): String {
|
||||
return getString(isUpper).let {
|
||||
if (isnotSpace) {
|
||||
it.replace("-", "")
|
||||
} else it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回标准格式的 UUID 字符串(带连字符)。
|
||||
*
|
||||
* @return 标准格式的 UUID 字符串
|
||||
*/
|
||||
// 优化后的 toString 方法
|
||||
fun getString(): String {
|
||||
return buildString(36) {
|
||||
val msbHigh = (mostSigBits ushr 32).toInt()
|
||||
val msbMid = ((mostSigBits ushr 16) and 0xFFFF).toInt()
|
||||
val msbLow = (mostSigBits and 0xFFFF).toInt()
|
||||
val lsbHigh = ((leastSigBits ushr 48) and 0xFFFF).toInt()
|
||||
val lsbLow = (leastSigBits and 0xFFFFFFFFFFFFL)
|
||||
|
||||
append(msbHigh.toHexString())
|
||||
append('-')
|
||||
append(msbMid.toShort().toHexString(4))
|
||||
append('-')
|
||||
append(msbLow.toShort().toHexString(4))
|
||||
append('-')
|
||||
append(lsbHigh.toShort().toHexString(4))
|
||||
append('-')
|
||||
append(lsbLow.toHexString(12))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将长整型转换为指定长度的十六进制字符串。
|
||||
*
|
||||
* @param length 字符串长度
|
||||
* @return 十六进制字符串
|
||||
*/
|
||||
private fun Long.toHexString(length: Int): String {
|
||||
return this.toString(16).padStart(length, '0')
|
||||
}
|
||||
|
||||
/**
|
||||
* 将短整型转换为指定长度的十六进制字符串。
|
||||
*
|
||||
* @param length 字符串长度
|
||||
* @return 十六进制字符串
|
||||
*/
|
||||
private fun Short.toHexString(length: Int): String {
|
||||
return this.toLong().and(0xFFFF).toString(16).padStart(length, '0')
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 MySQL 格式的 UUID 字节数组。
|
||||
*
|
||||
* @return MySQL 格式的 UUID 字节数组
|
||||
*/
|
||||
fun toMysql(): ByteArray {
|
||||
return mysqlToUuid(this.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 MySQL 格式的 UUID 实例。
|
||||
*
|
||||
* @return MySQL 格式的 UUID 实例
|
||||
*/
|
||||
fun toMysqlUUID(): UUID {
|
||||
return of(mysqlToUuid(this.data))
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个 UUID 是否相等。
|
||||
*
|
||||
* @param other 另一个对象
|
||||
* @return 如果相等则返回 true,否则返回 false
|
||||
*/
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
is UUID -> {
|
||||
this.data.contentEquals(other.data)
|
||||
}
|
||||
|
||||
is JUUID -> {
|
||||
other.mostSignificantBits == this.getMostSignificantBits() && other.leastSignificantBits == this.getLeastSignificantBits()
|
||||
}
|
||||
|
||||
else -> {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回对应的 Java UUID 实例。
|
||||
*
|
||||
* @return Java UUID 实例
|
||||
*/
|
||||
fun getUuid(): JUUID {
|
||||
return uuid
|
||||
return JUUID(mostSigBits, leastSigBits)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 UUID 转换为字符串表示,默认使用小写格式。
|
||||
* @param u 是否大写
|
||||
* @return UUID 字符串
|
||||
* 返回 UUID 的版本号。
|
||||
*
|
||||
* @return 版本号
|
||||
*/
|
||||
fun getString(u: Boolean): String {
|
||||
return if (u) {
|
||||
uuid.toString().uppercase(Locale.ROOT)
|
||||
} else {
|
||||
uuid.toString()
|
||||
fun getVersion(): Int {
|
||||
return version
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 UUID 的哈希码。
|
||||
*
|
||||
* @return 哈希码
|
||||
*/
|
||||
override fun hashCode(): Int {
|
||||
return data.contentHashCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 UUID 的字符串表示。
|
||||
*
|
||||
* @return 包含 UUID 和版本号的字符串
|
||||
*/
|
||||
override fun toString(): String {
|
||||
return "UUID(uuid=${getString()},version=${version})"
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 UUID 版本提取对应的时间信息。
|
||||
*
|
||||
* @return 对应的时间信息
|
||||
*/
|
||||
fun getDateTime(): DateTime {
|
||||
|
||||
when (version) {
|
||||
1 -> {
|
||||
val timestamp =
|
||||
((mostSigBits and 0x0FFFL) shl 48 or (((mostSigBits shr 16) and 0x0FFFFL) shl 32) or (mostSigBits ushr 32))
|
||||
val timestampBigInt = java.math.BigInteger.valueOf(timestamp)
|
||||
val nanosecondsBigInt = timestampBigInt.multiply(java.math.BigInteger.valueOf(100L))
|
||||
val divisor = java.math.BigInteger.valueOf(1_000_000_000L)
|
||||
val seconds = nanosecondsBigInt.divide(divisor)
|
||||
val nanos = nanosecondsBigInt.remainder(divisor)
|
||||
return DateTime.of(seconds.toLong(), nanos.toLong())
|
||||
.sub(DateTimeOffset.of(ChronoUnit.DAYS, UUID_EPOCH_OFFSET))
|
||||
}
|
||||
|
||||
6 -> {
|
||||
val timeHigh = (
|
||||
((data[0].toLong() and 0xFF) shl 40) or
|
||||
((data[1].toLong() and 0xFF) shl 32) or
|
||||
((data[2].toLong() and 0xFF) shl 24) or
|
||||
((data[3].toLong() and 0xFF) shl 16) or
|
||||
((data[4].toLong() and 0xFF) shl 8) or
|
||||
(data[5].toLong() and 0xFF)
|
||||
)
|
||||
val timeMidAndVersion = ((data[6].toInt() and 0xFF) shl 8) or (data[7].toInt() and 0xFF)
|
||||
val timeMid = timeMidAndVersion and 0x0FFF
|
||||
val hundredNanosSinceUuidEpoch = (timeHigh shl 12) or timeMid.toLong()
|
||||
val seconds = hundredNanosSinceUuidEpoch / 10_000_000
|
||||
val nanos = (hundredNanosSinceUuidEpoch % 10_000_000) * 100
|
||||
return DateTime.of(seconds, nanos).sub(DateTimeOffset.of(ChronoUnit.DAYS, UUID_EPOCH_OFFSET))
|
||||
}
|
||||
|
||||
7 -> {
|
||||
val times =
|
||||
(data[0].toLong() and 0xFF shl 40) or
|
||||
(data[1].toLong() and 0xFF shl 32) or
|
||||
(data[2].toLong() and 0xFF shl 24) or
|
||||
(data[3].toLong() and 0xFF shl 16) or
|
||||
(data[4].toLong() and 0xFF shl 8) or
|
||||
(data[5].toLong() and 0xFF)
|
||||
return DateTime.of(times)
|
||||
}
|
||||
|
||||
else -> {
|
||||
throw IllegalArgumentException("UUID version is $version not v1 or v6 or v7 : not supported ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 UUID 转换为字符串表示,默认使用小写格式。
|
||||
*
|
||||
* @return UUID 字符串
|
||||
*/
|
||||
fun getString(): String {
|
||||
return getString(false)
|
||||
}
|
||||
|
||||
@Deprecated("使用 getString()", ReplaceWith("getString"), level = DeprecationLevel.WARNING)
|
||||
fun toUUIDString(): String {
|
||||
return this.getString()
|
||||
}
|
||||
|
||||
@Deprecated("使用 getString(u:Boolean)", ReplaceWith("getString(u)"), level = DeprecationLevel.WARNING)
|
||||
fun toUUIDString(u: Boolean): String {
|
||||
return this.getString(u)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象。
|
||||
*
|
||||
* @return 对应的 DateTime 对象;如果 不是 时间戳V1版本 返回 null
|
||||
*/
|
||||
fun getDateTime(): DateTime? {
|
||||
if (uuid.version() != 1) {
|
||||
return null
|
||||
}
|
||||
return DateTime.of(uuid.timestamp() / 10000).add(
|
||||
DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从时间戳型 UUID 中提取 MAC 地址,默认使用冒号分隔符。
|
||||
* 提取 UUID V1 中的 MAC 地址,默认使用冒号分隔符。
|
||||
*
|
||||
* @return MAC 地址字符串
|
||||
*/
|
||||
fun extractMACFromUUID(): String {
|
||||
return extractMACFromUUID(null)
|
||||
fun getMac(): String {
|
||||
return getMac(":")
|
||||
}
|
||||
|
||||
fun getBase64ShortString(): String {
|
||||
return BASE64.encode(data).substring(0, 22)
|
||||
}
|
||||
|
||||
fun getBase91ShortString(): String {
|
||||
return BASE91.encode(data)
|
||||
}
|
||||
|
||||
fun getBase256ShortString(): String {
|
||||
return BASE256.encode(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从时间戳型 UUID 中提取 MAC 地址,并允许自定义分隔符。
|
||||
* 提取 UUID V1 中的 MAC 地址。
|
||||
*
|
||||
* @param spec 分隔符字符,默认为 ":"
|
||||
* @param spec 分隔符(默认为冒号)
|
||||
* @return MAC 地址字符串
|
||||
*/
|
||||
fun extractMACFromUUID(spec: String?): String {
|
||||
var spec = spec
|
||||
if (spec == null) {
|
||||
spec = ":"
|
||||
fun getMac(spec: String = ":"): String {
|
||||
if (version != 1) {
|
||||
throw IllegalArgumentException("UUID version is $version not v1 : not supported ")
|
||||
}
|
||||
val leastSigBits = uuid.leastSignificantBits
|
||||
val macLong = leastSigBits and 0xFFFFFFFFFFFFL
|
||||
val macBytes = ByteArray(6)
|
||||
for (i in 0..5) {
|
||||
@ -230,34 +690,4 @@ class UUID : Serializable {
|
||||
}
|
||||
return mac.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的字符串表示,包含版本信息和时间戳(如果是版本1)。
|
||||
*
|
||||
* @return UUID 的详细字符串表示
|
||||
*/
|
||||
override fun toString(): String {
|
||||
return "UUID(uuid=${getString()},version=${uuid.version()})"
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个 UUID 是否相等。
|
||||
*
|
||||
* @param other 比较对象
|
||||
* @return 如果相等返回 true,否则返回 false
|
||||
*/
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return uuid == other
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算此 UUID 的哈希码。
|
||||
*
|
||||
* @return 哈希码值
|
||||
*/
|
||||
override fun hashCode(): Int {
|
||||
return uuid.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
# ProjectName mingli-utils
|
||||
# ModuleName mingli-utils.main
|
||||
# CurrentFile org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
||||
# LastUpdate 2025-09-09 08:37:33
|
||||
# LastUpdate 2025-09-15 22:32:50
|
||||
# UpdateUser MingLiPro
|
||||
#
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user