From 8ef3cb5ba4c5790056297f3e40c9383a84179870 Mon Sep 17 00:00:00 2001 From: minglipro Date: Sun, 14 Sep 2025 22:10:47 +0800 Subject: [PATCH] =?UTF-8?q?refactor(kotlin):=20=E9=87=8D=E6=9E=84=20AES=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BB=E5=B9=B6=E4=BC=98=E5=8C=96=20Base64?= =?UTF-8?q?=20=E7=BC=96=E8=A7=A3=E7=A0=81=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 AesUtils.kt,使用 Kotlin 标准库的 encode 和 decode 方法替代自定义实现- 删除 Java 版本的 Base64Utils 类,迁移到 Kotlin 实现 - 重命名 ByteUtil.kt 为 ByteUtils.kt,统一命名风格 - 删除 Java 版本的 CloneUtil 类和 Factory 类,使用 Kotlin 实现 - 新增 Kotlin 版本的 SpringBeanUtils 工具类重构项目,迁移到 Kotlin - 将 Java 文件转换为 Kotlin 文件 - 更新项目结构和命名- 优化代码实现 - 添加必要的依赖 --- gradle.properties | 4 +- .../com/mingliqiye/utils/file/FileUtil.java | 13 +- .../minecraft/pe/json/Pseudocryptography.java | 31 ++- .../utils/network/NetworkAddress.java | 15 +- .../utils/network/NetworkEndpoint.java | 11 +- .../mingliqiye/utils/network/NetworkPort.java | 9 +- .../autoconfigure/AutoConfiguration.java | 117 ---------- .../autoconfigure/GsonAutoConfiguration.java | 73 ------ .../JacksonAutoConfiguration.java | 58 ----- .../mingliqiye/utils/system/SystemUtil.java | 221 ------------------ .../com/mingliqiye/utils/time/DateTime.java | 6 +- src/main/kotlin/com/mingliqiye/utils/Main.kt | 4 +- .../springboot/SpringBeanUtils.kt | 4 +- .../autoconfigure/AutoConfiguration.kt | 104 +++++++++ .../autoconfigure/GsonAutoConfiguration.kt | 91 ++++++++ .../autoconfigure/JacksonAutoConfiguration.kt | 76 ++++++ .../com/mingliqiye/utils/system/SystemUtil.kt | 195 ++++++++++++++++ 17 files changed, 527 insertions(+), 505 deletions(-) delete mode 100644 src/main/java/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.java delete mode 100644 src/main/java/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.java delete mode 100644 src/main/java/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.java delete mode 100644 src/main/java/com/mingliqiye/utils/system/SystemUtil.java rename src/main/kotlin/com/mingliqiye/utils/bean/{annotation => }/springboot/SpringBeanUtils.kt (97%) create mode 100644 src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.kt create mode 100644 src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.kt create mode 100644 src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.kt create mode 100644 src/main/kotlin/com/mingliqiye/utils/system/SystemUtil.kt diff --git a/gradle.properties b/gradle.properties index 224ee49..c1d0927 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,10 +16,10 @@ # ProjectName mingli-utils # ModuleName mingli-utils # CurrentFile gradle.properties -# LastUpdate 2025-09-13 10:14:51 +# LastUpdate 2025-09-14 22:10:29 # UpdateUser MingLiPro # JDKVERSIONS=1.8 GROUPSID=com.mingliqiye.utils ARTIFACTID=mingli-utils -VERSIONS=3.3.1 +VERSIONS=3.3.2 diff --git a/src/main/java/com/mingliqiye/utils/file/FileUtil.java b/src/main/java/com/mingliqiye/utils/file/FileUtil.java index b298e71..1c592ab 100644 --- a/src/main/java/com/mingliqiye/utils/file/FileUtil.java +++ b/src/main/java/com/mingliqiye/utils/file/FileUtil.java @@ -16,13 +16,16 @@ * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile FileUtil.java - * LastUpdate 2025-09-09 08:37:34 + * LastUpdate 2025-09-14 21:52:34 * UpdateUser MingLiPro */ package com.mingliqiye.utils.file; -import com.mingliqiye.utils.string.StringUtil; +import com.mingliqiye.utils.string.StringUtils; +import lombok.Getter; +import lombok.Setter; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -34,8 +37,6 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; -import lombok.Getter; -import lombok.Setter; /** * 文件工具类,提供常用的文件操作方法 @@ -243,7 +244,7 @@ public class FileUtil { * @return 文件扩展名(不包含点号),如果无扩展名返回空字符串 */ public static String getFileExtension(String fileName) { - if (StringUtil.isEmpty(fileName)) { + if (StringUtils.isEmpty(fileName)) { return ""; } int lastDotIndex = fileName.lastIndexOf('.'); @@ -260,7 +261,7 @@ public class FileUtil { * @return 不带扩展名的文件名 */ public static String getFileNameWithoutExtension(String fileName) { - if (StringUtil.isEmpty(fileName)) { + if (StringUtils.isEmpty(fileName)) { return ""; } int lastDotIndex = fileName.lastIndexOf('.'); diff --git a/src/main/java/com/mingliqiye/utils/minecraft/pe/json/Pseudocryptography.java b/src/main/java/com/mingliqiye/utils/minecraft/pe/json/Pseudocryptography.java index 7067fa1..f33e70f 100644 --- a/src/main/java/com/mingliqiye/utils/minecraft/pe/json/Pseudocryptography.java +++ b/src/main/java/com/mingliqiye/utils/minecraft/pe/json/Pseudocryptography.java @@ -1,15 +1,38 @@ +/* + * 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 Pseudocryptography.java + * LastUpdate 2025-09-14 21:53:30 + * UpdateUser MingLiPro + */ + package com.mingliqiye.utils.minecraft.pe.json; import com.mingliqiye.utils.collection.ForEach; import com.mingliqiye.utils.file.FileUtil; import com.mingliqiye.utils.json.JsonApi; import com.mingliqiye.utils.json.JsonTypeReference; -import com.mingliqiye.utils.string.StringUtil; +import com.mingliqiye.utils.string.StringUtils; +import lombok.val; + import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; -import lombok.val; public class Pseudocryptography { @@ -32,7 +55,7 @@ public class Pseudocryptography { return map2; } else if (object instanceof String) { - return StringUtil.stringToUnicode((String) object); + return StringUtils.stringToUnicode((String) object); } else if (object instanceof List) { ForEach.forEach((List) object, (t, i) -> { ((List) object).set(i, prossed(t)); @@ -49,7 +72,7 @@ public class Pseudocryptography { ); String s = jsonApi.format(prossed(map)).replace("\\\\u", "\\u"); - FileUtil.writeStringToFile(StringUtil.format("old-{}", path), s); + FileUtil.writeStringToFile(StringUtils.format("old-{}", path), s); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/mingliqiye/utils/network/NetworkAddress.java b/src/main/java/com/mingliqiye/utils/network/NetworkAddress.java index 73b2ff6..869a716 100644 --- a/src/main/java/com/mingliqiye/utils/network/NetworkAddress.java +++ b/src/main/java/com/mingliqiye/utils/network/NetworkAddress.java @@ -16,19 +16,20 @@ * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile NetworkAddress.java - * LastUpdate 2025-09-09 08:37:33 + * LastUpdate 2025-09-14 21:52:34 * UpdateUser MingLiPro */ package com.mingliqiye.utils.network; -import com.mingliqiye.utils.string.StringUtil; +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; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; /** * 网络地址类,用于表示一个网络地址(IP或域名),并提供相关操作。 @@ -182,7 +183,7 @@ public class NetworkAddress implements Serializable { // 不符合任一格式时抛出异常 throw new NetworkException( - StringUtil.format("[{}] 不是有效的IPv4或IPv6地址", ip) + StringUtils.format("[{}] 不是有效的IPv4或IPv6地址", ip) ); } @@ -206,12 +207,12 @@ public class NetworkAddress implements Serializable { */ public String toString() { return isdom - ? StringUtil.format( + ? StringUtils.format( "NetworkAddress(IP='{}',type='{}'," + "domain='{}')", ip, IPv, domain ) - : StringUtil.format("NetworkAddress(IP='{}',type='{}')", ip, IPv); + : StringUtils.format("NetworkAddress(IP='{}',type='{}')", ip, IPv); } } diff --git a/src/main/java/com/mingliqiye/utils/network/NetworkEndpoint.java b/src/main/java/com/mingliqiye/utils/network/NetworkEndpoint.java index 6b08195..8253ada 100644 --- a/src/main/java/com/mingliqiye/utils/network/NetworkEndpoint.java +++ b/src/main/java/com/mingliqiye/utils/network/NetworkEndpoint.java @@ -16,16 +16,17 @@ * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile NetworkEndpoint.java - * LastUpdate 2025-09-09 08:37:33 + * LastUpdate 2025-09-14 21:52:54 * UpdateUser MingLiPro */ package com.mingliqiye.utils.network; -import com.mingliqiye.utils.string.StringUtil; +import com.mingliqiye.utils.string.StringUtils; +import lombok.Getter; + import java.io.Serializable; import java.net.InetSocketAddress; -import lombok.Getter; /** * IP和端口聚集类,用于封装网络地址与端口信息。 @@ -121,7 +122,7 @@ public class NetworkEndpoint implements Serializable { * @return 格式化后的字符串 */ public String toHostPortString() { - return StringUtil.format( + return StringUtils.format( "{}:{}", networkAddress.getIp(), networkPort.getPort() @@ -135,7 +136,7 @@ public class NetworkEndpoint implements Serializable { * @return 包含详细信息的字符串 */ public String toString() { - return StringUtil.format( + return StringUtils.format( "NetworkEndpoint(IP={},Port={},Endpoint={})", networkAddress.getIp(), networkPort.getPort(), diff --git a/src/main/java/com/mingliqiye/utils/network/NetworkPort.java b/src/main/java/com/mingliqiye/utils/network/NetworkPort.java index 49367c5..9502f39 100644 --- a/src/main/java/com/mingliqiye/utils/network/NetworkPort.java +++ b/src/main/java/com/mingliqiye/utils/network/NetworkPort.java @@ -16,16 +16,17 @@ * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile NetworkPort.java - * LastUpdate 2025-09-09 08:37:33 + * LastUpdate 2025-09-14 21:53:06 * UpdateUser MingLiPro */ package com.mingliqiye.utils.network; -import com.mingliqiye.utils.string.StringUtil; -import java.io.Serializable; +import com.mingliqiye.utils.string.StringUtils; import lombok.Getter; +import java.io.Serializable; + /** * 网络端口类 * @@ -56,7 +57,7 @@ public class NetworkPort implements Serializable { // 验证端口号范围是否在0-65535之间 if (!(0 <= port && 65535 >= port)) { throw new NetworkException( - StringUtil.format("{} 不是正确的端口号", port) + StringUtils.format("{} 不是正确的端口号", port) ); } } diff --git a/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.java b/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.java deleted file mode 100644 index 38abd3e..0000000 --- a/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.java +++ /dev/null @@ -1,117 +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 AutoConfiguration.java - * LastUpdate 2025-09-13 01:23:09 - * UpdateUser MingLiPro - */ - -package com.mingliqiye.utils.springboot.autoconfigure; - -import com.mingliqiye.utils.collection.ForEach; -import com.mingliqiye.utils.collection.Lists; -import com.mingliqiye.utils.system.SystemUtil; -import com.mingliqiye.utils.time.DateTime; -import com.mingliqiye.utils.time.Formatter; -import lombok.val; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.ComponentScan; - -import java.io.IOException; -import java.io.InputStream; - -@org.springframework.boot.autoconfigure.AutoConfiguration -@EnableConfigurationProperties(AutoConfiguration.class) -@ComponentScan( - { - "com.mingliqiye.utils.bean.springboot", - "com.mingliqiye.utils.springboot.converters", - } -) -public class AutoConfiguration { - - private static final String banner = - "---------------------------------------------------------\n" + - "| $$\\ $$\\ $$\\ $$\\ $$\\ $$$$$$$$\\ $$$$$$\\ |\n" + - "| $$$\\ $$$ |$$ | $$ | $$ |\\__$$ __|$$ __$$\\ |\n" + - "| $$$$\\ $$$$ |$$ | $$ | $$ | $$ | $$ / \\__| |\n" + - "| $$\\$$\\$$ $$ |$$ | $$ | $$ | $$ | \\$$$$$$\\ |\n" + - "| $$ \\$$$ $$ |$$ | $$ | $$ | $$ | \\____$$\\ |\n" + - "| $$ |\\$ /$$ |$$ | $$ | $$ | $$ | $$\\ $$ | |\n" + - "| $$ | \\_/ $$ |$$$$$$$$\\\\$$$$$$ | $$ | \\$$$$$$ | |\n" + - "| \\__| \\__|\\________|\\______/ \\__| \\______/ |\n"; - - private static String banner2; - private final Logger log = LoggerFactory.getLogger( - "MingliUtils-AutoConfiguration" - ); - - public AutoConfiguration() { - printBanner(); - log.info("MingliUtils AutoConfiguration succeed"); - } - - public static void printBanner() { - StringBuilder bannerBuilder = new StringBuilder(banner); - try ( - InputStream inputStream = - AutoConfiguration.class.getResourceAsStream( - "/META-INF/meta-data" - ) - ) { - if (inputStream == null) { - return; - } - int readlen; - byte[] buffer = new byte[1024]; - StringBuilder metaData = new StringBuilder(); - while ((readlen = inputStream.read(buffer)) != -1) { - metaData.append(new String(buffer, 0, readlen)); - } - val da = Lists.toList(metaData.toString().split("\n")); - da.add("time=" + DateTime.now().format(Formatter.STANDARD_DATETIME_MILLISECOUND7)); - da.add("jdkRuntime=" + SystemUtil.getJdkVersion()); - ForEach.forEach(da, (s, i) -> { - String[] d = s.trim().split("=", 2); - if (d.length >= 2) { - String content = "| " + d[0] + ": " + d[1]; - int targetLength = 56; - if (content.length() < targetLength) { - bannerBuilder.append( - String.format("%-" + targetLength + "s|\n", content) - ); - } else { - bannerBuilder - .append(content, 0, targetLength) - .append("|\n"); - } - } - }); - } catch (IOException e) { - e.printStackTrace(); - } - banner2 = bannerBuilder.toString(); - System.out.print( - banner2 - ); - System.out.println( - "---------------------------------------------------------" - ); - } -} diff --git a/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.java b/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.java deleted file mode 100644 index 9a3a0f2..0000000 --- a/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.mingliqiye.utils.springboot.autoconfigure; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.mingliqiye.utils.json.GsonJsonApi; -import com.mingliqiye.utils.json.JsonApi; -import com.mingliqiye.utils.json.converters.DateTimeJsonConverter; -import com.mingliqiye.utils.json.converters.JsonStringConverter; -import com.mingliqiye.utils.json.converters.UUIDJsonStringConverter; -import com.mingliqiye.utils.time.DateTime; -import com.mingliqiye.utils.uuid.UUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer; -import org.springframework.context.annotation.Bean; - -@ConditionalOnClass(Gson.class) -@AutoConfiguration -@AutoConfigureAfter( - name = { - "org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration", - "com.mingliqiye.utils.springboot.autoconfigure.JacksonAutoConfiguration", - } -) -public class GsonAutoConfiguration { - - private static final Logger log = LoggerFactory.getLogger( - "MingliUtils-GsonAutoConfiguration" - ); - - public static GsonBuilder addTypeAdapter(GsonBuilder gsonBuilder) { - JsonStringConverter dateTimeJsonConverter = - new DateTimeJsonConverter(); - JsonStringConverter uuidJsonStringConverter = - new UUIDJsonStringConverter(); - - try { - return gsonBuilder - .registerTypeAdapter( - uuidJsonStringConverter.getTClass(), - dateTimeJsonConverter - .getGsonJsonStringConverterAdapter() - .getGsonTypeAdapter() - ) - .registerTypeAdapter( - dateTimeJsonConverter.getTClass(), - dateTimeJsonConverter - .getGsonJsonStringConverterAdapter() - .getGsonTypeAdapter() - ); - } finally { - log.info("MingliUtils GsonBuilder TypeAdapter add"); - } - } - - @Bean - public GsonBuilderCustomizer mingliGsonCustomizer() { - return GsonAutoConfiguration::addTypeAdapter; - } - - @Bean - @ConditionalOnMissingBean - public JsonApi jsonApi(Gson gson) { - log.info( - "MingliUtils-JsonApiAutoConfiguration: GsonJsonApi bean is created." - ); - return new GsonJsonApi(gson); - } -} diff --git a/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.java b/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.java deleted file mode 100644 index d4e93b8..0000000 --- a/src/main/java/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.mingliqiye.utils.springboot.autoconfigure; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mingliqiye.utils.json.JacksonJsonApi; -import com.mingliqiye.utils.json.JsonApi; -import com.mingliqiye.utils.json.converters.DateTimeJsonConverter; -import com.mingliqiye.utils.json.converters.UUIDJsonStringConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; - -@ConditionalOnClass(ObjectMapper.class) -@AutoConfiguration -@AutoConfigureAfter( - name = { - "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration", - } -) -public class JacksonAutoConfiguration { - - private static final Logger log = LoggerFactory.getLogger( - "MingliUtils-JacksonAutoConfiguration" - ); - - public JacksonAutoConfiguration(ObjectMapper objectMapper) { - addModules(objectMapper); - log.info("MingliUtils Jackson Serializers created"); - } - - public static ObjectMapper addModules(ObjectMapper objectMapper) { - return objectMapper - .registerModule( - new DateTimeJsonConverter() - .getJacksonJsonStringConverterAdapter() - .getJacksonModule() - ) - .registerModule( - new UUIDJsonStringConverter() - .getJacksonJsonStringConverterAdapter() - .getJacksonModule() - ); - } - - @Bean - @Primary - @ConditionalOnMissingBean - public JsonApi jsonApi(ObjectMapper objectMapper) { - log.info( - "MingliUtils-JsonApiAutoConfiguration: JacksonJsonApi bean is created." - ); - return new JacksonJsonApi(objectMapper); - } -} diff --git a/src/main/java/com/mingliqiye/utils/system/SystemUtil.java b/src/main/java/com/mingliqiye/utils/system/SystemUtil.java deleted file mode 100644 index 5ed0c47..0000000 --- a/src/main/java/com/mingliqiye/utils/system/SystemUtil.java +++ /dev/null @@ -1,221 +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 SystemUtil.java - * LastUpdate 2025-09-09 08:37:34 - * UpdateUser MingLiPro - */ - -package com.mingliqiye.utils.system; - -import com.mingliqiye.utils.collection.Lists; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; - -/** - * 系统工具类,提供操作系统类型判断和JDK版本检测功能 - * - * @author MingLiPro - */ -public class SystemUtil { - - private static final String osName = System.getProperties().getProperty( - "os.name" - ); - - /** - * 判断当前操作系统是否为Windows系统 - * - * @return 如果是Windows系统返回true,否则返回false - */ - public static boolean isWindows() { - return osName != null && osName.startsWith("Windows"); - } - - /** - * 判断当前操作系统是否为Mac系统 - * - * @return 如果是Mac系统返回true,否则返回false - */ - public static boolean isMac() { - return osName != null && osName.startsWith("Mac"); - } - - /** - * 判断当前操作系统是否为Unix/Linux系统 - * - * @return 如果是Unix/Linux系统返回true,否则返回false - */ - public static boolean isUnix() { - if (osName == null) { - return false; - } - return ( - osName.startsWith("Linux") || - osName.startsWith("AIX") || - osName.startsWith("SunOS") || - osName.startsWith("Mac OS X") || - osName.startsWith("FreeBSD") - ); - } - - /** - * 获取JDK版本号 - * - * @return JDK版本号字符串 - */ - public static String getJdkVersion() { - return System.getProperty("java.specification.version"); - } - - /** - * 获取Java版本号的整数形式 - * - * @return Java版本号的整数形式(如:8、11、17等) - */ - public static Integer getJavaVersionAsInteger() { - String version = getJdkVersion(); - if (version == null || version.isEmpty()) { - throw new IllegalStateException( - "Unable to determine Java version from property 'java.specification.version'" - ); - } - - String uversion; - if (version.startsWith("1.")) { - if (version.length() < 3) { - throw new IllegalStateException( - "Invalid Java version format: " + version - ); - } - uversion = version.substring(2, 3); - } else { - if (version.length() < 2) { - throw new IllegalStateException( - "Invalid Java version format: " + version - ); - } - uversion = version.substring(0, 2); - } - return Integer.parseInt(uversion); - } - - /** - * 判断当前JDK版本是否大于8 - * - * @return 如果JDK版本大于8返回true,否则返回false - */ - public static boolean isJdk8Plus() { - return getJavaVersionAsInteger() > 8; - } - - /** - * 获取本地IP地址数组 - * - * @return 本地IP地址字符串数组 - * @throws RuntimeException 当获取网络接口信息失败时抛出 - */ - public static String[] getLocalIps() { - try { - List ipList = new ArrayList<>(); - Enumeration interfaces = - NetworkInterface.getNetworkInterfaces(); - - while (interfaces.hasMoreElements()) { - NetworkInterface networkInterface = interfaces.nextElement(); - // 跳过回环接口和虚拟接口 - if ( - networkInterface.isLoopback() || - networkInterface.isVirtual() || - !networkInterface.isUp() - ) { - continue; - } - - Enumeration addresses = - networkInterface.getInetAddresses(); - while (addresses.hasMoreElements()) { - InetAddress address = addresses.nextElement(); - // 只获取IPv4地址 - if (address instanceof Inet4Address) { - ipList.add(address.getHostAddress()); - } - } - } - - return ipList.toArray(new String[0]); - } catch (SocketException e) { - throw new RuntimeException("Failed to get local IP addresses", e); - } - } - - /** - * 获取本地IP地址列表 - * - * @return 本地IP地址的字符串列表 - */ - public static List getLocalIpsByList() { - return Lists.newArrayList(getLocalIps()); - } - - /** - * 获取本地回环地址 - * - * @return 回环地址字符串,通常为"127.0.0.1" - */ - public static String[] getLoopbackIps() { - List strings = new ArrayList<>(3); - try { - Enumeration interfaces = - NetworkInterface.getNetworkInterfaces(); - - while (interfaces.hasMoreElements()) { - NetworkInterface networkInterface = interfaces.nextElement(); - - // 只处理回环接口 - if (networkInterface.isLoopback() && networkInterface.isUp()) { - Enumeration addresses = - networkInterface.getInetAddresses(); - - while (addresses.hasMoreElements()) { - InetAddress address = addresses.nextElement(); - strings.add(address.getHostAddress()); - } - } - } - return strings.toArray(new String[0]); - } catch (SocketException e) { - // 可考虑添加日志记录 - return new String[] { "127.0.0.1" }; - } - } - - /** - * 获取本地回环地址IP列表 - * - * @return 本地回环地址IP字符串列表的副本 - */ - public static List getLoopbackIpsByList() { - // 将本地回环地址IP数组转换为列表并返回 - return Lists.newArrayList(getLoopbackIps()); - } -} diff --git a/src/main/java/com/mingliqiye/utils/time/DateTime.java b/src/main/java/com/mingliqiye/utils/time/DateTime.java index 5713eaa..b05ae23 100644 --- a/src/main/java/com/mingliqiye/utils/time/DateTime.java +++ b/src/main/java/com/mingliqiye/utils/time/DateTime.java @@ -16,7 +16,7 @@ * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile DateTime.java - * LastUpdate 2025-09-13 10:14:09 + * LastUpdate 2025-09-14 22:05:19 * UpdateUser MingLiPro */ @@ -24,7 +24,7 @@ package com.mingliqiye.utils.time; import com.mingliqiye.utils.jna.WinKernel32Api; import com.mingliqiye.utils.jna.WinKernel32ApiFactory; -import com.mingliqiye.utils.system.SystemUtil; +import com.mingliqiye.utils.system.SystemUtils; import lombok.Getter; import lombok.Setter; import lombok.val; @@ -65,7 +65,7 @@ public final class DateTime implements Serializable { private static final WinKernel32Api WIN_KERNEL_32_API; static { - if (SystemUtil.getJavaVersionAsInteger() == 8 && SystemUtil.isWindows()) { + if (SystemUtils.getJavaVersionAsInteger() == 8 && SystemUtils.isWindows()) { final Logger log = getMingLiLoggerFactory().getLogger( "mingli-utils DateTime" diff --git a/src/main/kotlin/com/mingliqiye/utils/Main.kt b/src/main/kotlin/com/mingliqiye/utils/Main.kt index 82dded7..ad0cd8e 100644 --- a/src/main/kotlin/com/mingliqiye/utils/Main.kt +++ b/src/main/kotlin/com/mingliqiye/utils/Main.kt @@ -16,7 +16,7 @@ * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile Main.kt - * LastUpdate 2025-09-14 20:08:44 + * LastUpdate 2025-09-14 22:05:03 * UpdateUser MingLiPro */ @@ -29,6 +29,4 @@ import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration fun main() { AutoConfiguration.printBanner() - - 0..10 } diff --git a/src/main/kotlin/com/mingliqiye/utils/bean/annotation/springboot/SpringBeanUtils.kt b/src/main/kotlin/com/mingliqiye/utils/bean/springboot/SpringBeanUtils.kt similarity index 97% rename from src/main/kotlin/com/mingliqiye/utils/bean/annotation/springboot/SpringBeanUtils.kt rename to src/main/kotlin/com/mingliqiye/utils/bean/springboot/SpringBeanUtils.kt index 1a44b9a..6cbc38f 100644 --- a/src/main/kotlin/com/mingliqiye/utils/bean/annotation/springboot/SpringBeanUtils.kt +++ b/src/main/kotlin/com/mingliqiye/utils/bean/springboot/SpringBeanUtils.kt @@ -16,11 +16,11 @@ * ProjectName mingli-utils * ModuleName mingli-utils.main * CurrentFile SpringBeanUtils.kt - * LastUpdate 2025-09-14 20:01:26 + * LastUpdate 2025-09-14 22:10:45 * UpdateUser MingLiPro */ -package com.mingliqiye.utils.bean.annotation.springboot +package com.mingliqiye.utils.bean.springboot import org.springframework.beans.BeansException import org.springframework.context.ApplicationContext diff --git a/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.kt b/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.kt new file mode 100644 index 0000000..8b6081d --- /dev/null +++ b/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/AutoConfiguration.kt @@ -0,0 +1,104 @@ +/* + * 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 AutoConfiguration.kt + * LastUpdate 2025-09-14 22:09:46 + * UpdateUser MingLiPro + */ + +package com.mingliqiye.utils.springboot.autoconfigure + +import com.mingliqiye.utils.collection.ForEach +import com.mingliqiye.utils.logger.mingLiLoggerFactory +import com.mingliqiye.utils.system.getJdkVersion +import com.mingliqiye.utils.time.DateTime +import com.mingliqiye.utils.time.Formatter +import org.springframework.context.annotation.ComponentScan +import java.io.IOException + +@org.springframework.boot.autoconfigure.AutoConfiguration +@ComponentScan( + "com.mingliqiye.utils.bean.springboot", + "com.mingliqiye.utils.springboot.converters" +) +open class AutoConfiguration { + companion object { + private const val banner = + "---------------------------------------------------------\n" + + "| $$\\ $$\\ $$\\ $$\\ $$\\ $$$$$$$$\\ $$$$$$\\ |\n" + + "| $$$\\ $$$ |$$ | $$ | $$ |\\__$$ __|$$ __$$\\ |\n" + + "| $$$$\\ $$$$ |$$ | $$ | $$ | $$ | $$ / \\__| |\n" + + "| $$\\$$\\$$ $$ |$$ | $$ | $$ | $$ | \\$$$$$$\\ |\n" + + "| $$ \\$$$ $$ |$$ | $$ | $$ | $$ | \\____$$\\ |\n" + + "| $$ |\\$ /$$ |$$ | $$ | $$ | $$ | $$\\ $$ | |\n" + + "| $$ | \\_/ $$ |$$$$$$$$\\\\$$$$$$ | $$ | \\$$$$$$ | |\n" + + "| \\__| \\__|\\________|\\______/ \\__| \\______/ |\n" + + private var banner2: String? = null + private val log = mingLiLoggerFactory.getLogger("MingliUtils-AutoConfiguration") + + fun printBanner() { + val bannerBuilder = StringBuilder(banner) + try { + val inputStream = AutoConfiguration::class.java.getResourceAsStream("/META-INF/meta-data") + if (inputStream == null) { + return + } + inputStream.use { stream -> + var readlen: Int + val buffer = ByteArray(1024) + val metaData = StringBuilder() + while (stream.read(buffer).also { readlen = it } != -1) { + 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()) + ForEach.forEach(da) { s: String, _: Int -> + val d = s.trim { it <= ' ' }.split("=".toRegex(), 2).toTypedArray() + if (d.size >= 2) { + val content = "| " + d[0] + ": " + d[1] + val targetLength = 56 + if (content.length < targetLength) { + bannerBuilder.append( + String.format( + "%-" + targetLength + "s|\n", + content + ) + ) + } else { + bannerBuilder + .append(content, 0, targetLength) + .append("|\n") + } + } + } + } + } catch (e: IOException) { + e.printStackTrace() + } + banner2 = bannerBuilder.toString() + println(banner2) + println("---------------------------------------------------------") + } + } + + init { + printBanner() + log.info("MingliUtils AutoConfiguration succeed") + } +} diff --git a/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.kt b/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.kt new file mode 100644 index 0000000..47c2381 --- /dev/null +++ b/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/GsonAutoConfiguration.kt @@ -0,0 +1,91 @@ +/* + * 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 GsonAutoConfiguration.kt + * LastUpdate 2025-09-14 22:06:47 + * UpdateUser MingLiPro + */ + +package com.mingliqiye.utils.springboot.autoconfigure + +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.mingliqiye.utils.json.GsonJsonApi +import com.mingliqiye.utils.json.JsonApi +import com.mingliqiye.utils.json.converters.DateTimeJsonConverter +import com.mingliqiye.utils.json.converters.JsonStringConverter +import com.mingliqiye.utils.json.converters.UUIDJsonStringConverter +import com.mingliqiye.utils.logger.mingLiLoggerFactory +import com.mingliqiye.utils.time.DateTime +import com.mingliqiye.utils.uuid.UUID +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.AutoConfiguration +import org.springframework.boot.autoconfigure.AutoConfigureAfter +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer +import org.springframework.context.annotation.Bean + +@ConditionalOnClass(Gson::class) +@AutoConfiguration +@AutoConfigureAfter( + name = ["org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration", + "com.mingliqiye.utils.springboot.autoconfigure.JacksonAutoConfiguration"] +) +open class GsonAutoConfiguration { + companion object { + private val log: Logger = LoggerFactory.getLogger("MingliUtils-GsonAutoConfiguration") + + fun addTypeAdapter(gsonBuilder: GsonBuilder): GsonBuilder { + val dateTimeJsonConverter: JsonStringConverter = DateTimeJsonConverter() + val uuidJsonStringConverter: JsonStringConverter = UUIDJsonStringConverter() + + try { + return gsonBuilder + .registerTypeAdapter( + uuidJsonStringConverter.getTClass(), + dateTimeJsonConverter + .getGsonJsonStringConverterAdapter() + .getGsonTypeAdapter() + ) + .registerTypeAdapter( + dateTimeJsonConverter.getTClass(), + dateTimeJsonConverter + .getGsonJsonStringConverterAdapter() + .getGsonTypeAdapter() + ) + } finally { + log.info("MingliUtils GsonBuilder TypeAdapter add") + } + } + } + + private val log: Logger = mingLiLoggerFactory.getLogger("MingliUtils-GsonAutoConfiguration") + + @Bean + open fun mingliGsonCustomizer(): GsonBuilderCustomizer { + return GsonBuilderCustomizer { gsonBuilder: GsonBuilder -> addTypeAdapter(gsonBuilder) } + } + + @Bean + @ConditionalOnMissingBean + open fun jsonApi(gson: Gson): JsonApi { + log.info("MingliUtils-JsonApiAutoConfiguration: GsonJsonApi bean is created.") + return GsonJsonApi(gson) + } +} diff --git a/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.kt b/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.kt new file mode 100644 index 0000000..1aeb747 --- /dev/null +++ b/src/main/kotlin/com/mingliqiye/utils/springboot/autoconfigure/JacksonAutoConfiguration.kt @@ -0,0 +1,76 @@ +/* + * 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 JacksonAutoConfiguration.kt + * LastUpdate 2025-09-14 22:10:08 + * UpdateUser MingLiPro + */ + +package com.mingliqiye.utils.springboot.autoconfigure + +import com.fasterxml.jackson.databind.ObjectMapper +import com.mingliqiye.utils.json.JacksonJsonApi +import com.mingliqiye.utils.json.JsonApi +import com.mingliqiye.utils.json.converters.DateTimeJsonConverter +import com.mingliqiye.utils.json.converters.UUIDJsonStringConverter +import com.mingliqiye.utils.logger.mingLiLoggerFactory +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.AutoConfiguration +import org.springframework.boot.autoconfigure.AutoConfigureAfter +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Primary + +@ConditionalOnClass(ObjectMapper::class) +@AutoConfiguration +@AutoConfigureAfter(org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration::class) +open class JacksonAutoConfiguration(objectMapper: ObjectMapper) { + companion object { + private val log: Logger = mingLiLoggerFactory.getLogger("MingliUtils-JacksonAutoConfiguration") + + fun addModules(objectMapper: ObjectMapper): ObjectMapper { + return objectMapper + .registerModule( + DateTimeJsonConverter() + .jacksonJsonStringConverterAdapter + .getJacksonModule() + ) + .registerModule( + UUIDJsonStringConverter() + .jacksonJsonStringConverterAdapter + .getJacksonModule() + ) + } + } + + private val log: Logger = LoggerFactory.getLogger("MingliUtils-JacksonAutoConfiguration") + + init { + addModules(objectMapper) + log.info("MingliUtils Jackson Serializers created") + } + + @Bean + @Primary + @ConditionalOnMissingBean + open fun jsonApi(objectMapper: ObjectMapper): JsonApi { + log.info("MingliUtils-JsonApiAutoConfiguration: JacksonJsonApi bean is created.") + return JacksonJsonApi(objectMapper) + } +} diff --git a/src/main/kotlin/com/mingliqiye/utils/system/SystemUtil.kt b/src/main/kotlin/com/mingliqiye/utils/system/SystemUtil.kt new file mode 100644 index 0000000..0900fbd --- /dev/null +++ b/src/main/kotlin/com/mingliqiye/utils/system/SystemUtil.kt @@ -0,0 +1,195 @@ +/* + * 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 SystemUtil.kt + * LastUpdate 2025-09-14 21:56:58 + * UpdateUser MingLiPro + */ +@file:JvmName("SystemUtils") + +package com.mingliqiye.utils.system + +import com.mingliqiye.utils.collection.Lists +import java.net.Inet4Address +import java.net.NetworkInterface +import java.net.SocketException + +private val osName: String? = System.getProperties().getProperty("os.name") + +/** + * 判断当前操作系统是否为Windows系统 + * + * @return 如果是Windows系统返回true,否则返回false + */ +fun isWindows(): Boolean { + return osName != null && osName.startsWith("Windows") +} + +/** + * 判断当前操作系统是否为Mac系统 + * + * @return 如果是Mac系统返回true,否则返回false + */ +fun isMac(): Boolean { + return osName != null && osName.startsWith("Mac") +} + +/** + * 判断当前操作系统是否为Unix/Linux系统 + * + * @return 如果是Unix/Linux系统返回true,否则返回false + */ +fun isUnix(): Boolean { + if (osName == null) { + return false + } + return (osName.startsWith("Linux") || osName.startsWith("AIX") || osName.startsWith("SunOS") || osName.startsWith("Mac OS X") || osName.startsWith( + "FreeBSD" + )) +} + +/** + * 获取JDK版本号 + * + * @return JDK版本号字符串 + */ +fun getJdkVersion(): String { + return System.getProperty("java.specification.version") +} + +/** + * 获取Java版本号的整数形式 + * + * @return Java版本号的整数形式(如:8、11、17等) + */ +fun getJavaVersionAsInteger(): Int { + val version = getJdkVersion() + if (version == null || version.isEmpty()) { + throw IllegalStateException( + "Unable to determine Java version from property 'java.specification.version'" + ) + } + + val uversion: String = if (version.startsWith("1.")) { + if (version.length < 3) { + throw IllegalStateException( + "Invalid Java version format: $version" + ) + } + version.substring(2, 3) + } else { + if (version.length < 2) { + throw IllegalStateException( + "Invalid Java version format: $version" + ) + } + version.substring(0, 2) + } + return uversion.toInt() +} + +/** + * 判断当前JDK版本是否大于8 + * + * @return 如果JDK版本大于8返回true,否则返回false + */ +fun isJdk8Plus(): Boolean { + return getJavaVersionAsInteger() > 8 +} + +/** + * 获取本地IP地址数组 + * + * @return 本地IP地址字符串数组 + * @throws RuntimeException 当获取网络接口信息失败时抛出 + */ +fun getLocalIps(): Array { + return try { + val ipList: MutableList = ArrayList() + val interfaces = NetworkInterface.getNetworkInterfaces() + + while (interfaces.hasMoreElements()) { + val networkInterface = interfaces.nextElement() + // 跳过回环接口和虚拟接口 + if (networkInterface.isLoopback || networkInterface.isVirtual || !networkInterface.isUp) { + continue + } + + val addresses = networkInterface.inetAddresses + while (addresses.hasMoreElements()) { + val address = addresses.nextElement() + // 只获取IPv4地址 + if (address is Inet4Address) { + ipList.add(address.hostAddress) + } + } + } + + ipList.toTypedArray() + } catch (e: SocketException) { + throw RuntimeException("Failed to get local IP addresses", e) + } +} + +/** + * 获取本地IP地址列表 + * + * @return 本地IP地址的字符串列表 + */ +fun getLocalIpsByList(): List { + return Lists.newArrayList(*getLocalIps()) +} + +/** + * 获取本地回环地址 + * + * @return 回环地址字符串,通常为"127.0.0.1" + */ +fun getLoopbackIps(): Array { + val strings: MutableList = ArrayList(3) + return try { + val interfaces = NetworkInterface.getNetworkInterfaces() + + while (interfaces.hasMoreElements()) { + val networkInterface = interfaces.nextElement() + + // 只处理回环接口 + if (networkInterface.isLoopback && networkInterface.isUp) { + val addresses = networkInterface.inetAddresses + + while (addresses.hasMoreElements()) { + val address = addresses.nextElement() + strings.add(address.hostAddress) + } + } + } + strings.toTypedArray() + } catch (e: SocketException) { + // 可考虑添加日志记录 + arrayOf("127.0.0.1") + } +} + +/** + * 获取本地回环地址IP列表 + * + * @return 本地回环地址IP字符串列表的副本 + */ +fun getLoopbackIpsByList(): List { + // 将本地回环地址IP数组转换为列表并返回 + return Lists.newArrayList(*getLoopbackIps()) +}