From eefb219e438177adf07714f8e7a5d3b964adacaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=8F=E5=87=A1?= Date: Tue, 13 Jun 2023 15:36:18 +0800 Subject: [PATCH 01/26] =?UTF-8?q?=E4=B8=8D=E8=A7=A3=E5=8E=8Bzip=EF=BC=8C?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E6=96=87=E4=BB=B6=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/compress/ZipReplacer.java | 278 ++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java new file mode 100644 index 000000000..2e72205b0 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java @@ -0,0 +1,278 @@ +package org.dromara.hutool.core.compress; + +import org.dromara.hutool.core.io.IORuntimeException; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.CharsetUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.regex.Matcher; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +public class ZipReplacer { + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFile 源文件 + * @param tarFile 输出文件 + * @param innerFiles 替换文件在zip中的相对路径 + * @param replaceFiles 替换文件到系统中到存储路径 + * @param charset 读取编码格式 + * @param charsetOut 输出编码格式 + */ + public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles, Charset charset, Charset charsetOut) { +// 记录zip中是否存在相同路径的文件, 是 更新文件 否,添加文件 + boolean[] updates = new boolean[replaceFiles.length]; + try (ZipFile zipFile = new ZipFile(srcFile, charset); + FileOutputStream fos = new FileOutputStream(tarFile); + ZipOutputStream zos = new ZipOutputStream(fos); + ZipWriter zipWriter = new ZipWriter(zos, charsetOut)) { + HashMap data = new HashMap<>(); + for (Enumeration entries = zipFile.entries(); entries.hasMoreElements(); ) { + ZipEntry zipEntryIn = entries.nextElement(); + String zipEntryInName = zipEntryIn.getName(); +// false 未被替换 + boolean update = false; + for (int i = 0; i < innerFiles.length; i++) { + update = samePath(zipEntryInName, innerFiles[i]); +// 存在同路径文件,替换 + if (update) { + updates[i] = true; + data.put(zipEntryInName, FileUtil.getInputStream(replaceFiles[i])); + break; + } + } +// 不存在同路径文件,直接添加原zipEntry + if (!update) { + data.put(zipEntryInName, ZipUtil.getStream(zipFile, zipEntryIn)); + } + } +// 确认replaceFiles是否替换原zip中文件,没有替换直接添加 + for (int i = 0; i < updates.length; i++) { + if (!updates[i]) { +// 原zip中不存在同路径文件,添加到制定目录 + data.put(innerFiles[i], FileUtil.getInputStream(replaceFiles[i])); + } + } + for (String key : data.keySet()) { + zipWriter.add(key, data.get(key)); + } + } catch (IOException e) { + throw new IORuntimeException(e); + } + } + + public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles, Charset charset, Charset charsetOut) { + File[] files = replaceFiles.toArray(new File[0]); + replace(srcFile, tarFile, innerFiles, files, charset, charsetOut); + } + + public static void replace(File srcFile, File tarFile, String[] innerFiles, String[] replaceFiles, Charset charset, Charset charsetOut) { + List files = Arrays.stream(replaceFiles).map(FileUtil::file).collect(Collectors.toList()); + replace(srcFile, tarFile, innerFiles, files, charset, charsetOut); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFile 源文件 + * @param tarFile 输出文件 + * @param innerFiles 替换文件在zip中的相对路径 + * @param replaceFiles 替换文件到系统中到存储路径 + * @param charset 读取输出编码格式 + */ + public static void replace(File srcFile, File tarFile, String[] innerFiles, String[] replaceFiles, Charset charset) { + replace(srcFile, tarFile, innerFiles, replaceFiles, charset, charset); + } + + public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles, Charset charset) { + replace(srcFile, tarFile, innerFiles, replaceFiles, charset, charset); + } + + public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles, Charset charset) { + replace(srcFile, tarFile, innerFiles, replaceFiles, charset, charset); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFile 源文件 + * @param tarFile 输出文件 + * @param innerFiles 要替换文件在zip中的相对路径 + * @param replaceFiles 替换文件到系统中到存储路径 + */ + public static void replace(File srcFile, File tarFile, String[] innerFiles, String[] replaceFiles) { + replace(srcFile, tarFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles) { + replace(srcFile, tarFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles) { + replace(srcFile, tarFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFile 源文件,也是输出路径 + * @param innerFiles 替换文件在zip中的相对路径 + * @param replaceFiles 替换文件到系统中到存储路径 + * @param charset 读取输出编码格式 + */ + public static void replace(File srcFile, String[] innerFiles, String[] replaceFiles, Charset charset) { + replace(srcFile, srcFile, innerFiles, replaceFiles, charset); + } + + public static void replace(File srcFile, String[] innerFiles, File[] replaceFiles, Charset charset) { + replace(srcFile, srcFile, innerFiles, replaceFiles, charset); + } + + public static void replace(File srcFile, String[] innerFiles, List replaceFiles, Charset charset) { + replace(srcFile, srcFile, innerFiles, replaceFiles, charset); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFile 源文件 + * @param innerFiles 要替换文件在zip中的相对路径 + * @param replaceFiles 要替换的文件绝对路径 + */ + public static void replace(File srcFile, String[] innerFiles, String[] replaceFiles) { + replace(srcFile, srcFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(File srcFile, String[] innerFiles, File[] replaceFiles) { + replace(srcFile, srcFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(File srcFile, String[] innerFiles, List replaceFiles) { + replace(srcFile, srcFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFilePath 源文件路径 + * @param tarFilePath 输出文件路径 + * @param innerFiles 要替换文件在zip中的相对路径 + * @param replaceFiles 要替换的文件绝对路径 + * @param charset 读取编码格式 + * @param charsetOut 输出编码格式 + */ + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, String[] replaceFiles, Charset charset, Charset charsetOut) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charsetOut); + } + + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, File[] replaceFiles, Charset charset, Charset charsetOut) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charsetOut); + } + + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, List replaceFiles, Charset charset, Charset charsetOut) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charsetOut); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFilePath zip源文件路径 + * @param tarFilePath zip输出文件路径 + * @param innerFiles 要替换文件在zip中的相对路径 + * @param replaceFiles 要替换的文件绝对路径 + * @param charset 读取输出编码格式 + */ + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, String[] replaceFiles, Charset charset) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charset); + } + + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, File[] replaceFiles, Charset charset) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charset); + } + + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, List replaceFiles, Charset charset) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charset); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFilePath 源文件路径 + * @param tarFilePath 输出文件路径 + * @param innerFiles 替换文件在zip中的相对路径 + * @param replaceFiles 替换文件到系统中到存储路径 + */ + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, String[] replaceFiles) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, File[] replaceFiles) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, List replaceFiles) { + replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + /** + * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * + * @param srcFilePath 源文件路径,也是输出路径 + * @param innerFiles 要替换文件在zip中的相对路径 + * @param replaceFiles 要替换的文件绝对路径 + */ + public static void replace(String srcFilePath, String[] innerFiles, String[] replaceFiles) { + replace(FileUtil.file(srcFilePath), FileUtil.file(srcFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(String srcFilePath, String[] innerFiles, File[] replaceFiles) { + replace(FileUtil.file(srcFilePath), FileUtil.file(srcFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + public static void replace(String srcFilePath, String[] innerFiles, List replaceFiles) { + replace(FileUtil.file(srcFilePath), FileUtil.file(srcFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + } + + /** + * 判断路径是否相等 + * + * @param entryPath 路径A + * @param targetPath 路径B + * @param ignoreCase 是否忽略大小写 + * @return ture 路径相等 + */ + public static boolean samePath(String entryPath, String targetPath, boolean ignoreCase) { + + entryPath = entryPath.replaceAll("[/\\\\]", Matcher.quoteReplacement(File.separator)); + targetPath = targetPath.replaceAll("[/\\\\]", Matcher.quoteReplacement(File.separator)); + if (entryPath.startsWith(File.separator)) { + entryPath = entryPath.substring(1); + } + if (targetPath.startsWith(File.separator)) { + targetPath = targetPath.substring(1); + } + + if (ignoreCase) { + return StrUtil.equalsIgnoreCase(entryPath, targetPath); + } else { + return StrUtil.equals(entryPath, targetPath); + } + } + + public static boolean samePath(String entryPath, String targetPath) { + return samePath(entryPath, targetPath, true); + } +} From ff2930cba38e062ddc877852feb086eb70272d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=8F=E5=87=A1?= Date: Tue, 13 Jun 2023 15:44:40 +0800 Subject: [PATCH 02/26] =?UTF-8?q?=E7=94=A8List=E5=8F=82=E6=95=B0=E5=8E=BB?= =?UTF-8?q?=E8=B0=83=E7=94=A8array=E5=8F=82=E6=95=B0=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95,=20=E8=BF=99=E4=B8=AA=E6=96=B9=E6=B3=95=E5=A4=8D?= =?UTF-8?q?=E7=94=A8=E9=80=BB=E8=BE=91=E6=98=AF=E4=B8=8D=E5=A4=AA=E5=A5=BD?= =?UTF-8?q?=E7=9A=84,=20List=E8=BD=ACarray=E4=BC=9A=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=96=B0=E6=95=B0=E7=BB=84,=20=E9=80=A0=E6=88=90=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E6=B5=AA=E8=B4=B9.=20=E5=A6=82=E6=9E=9C=E5=8F=8D?= =?UTF-8?q?=E8=BF=87=E6=9D=A5,=20array=E8=BD=ACList,=20=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E4=BD=BF=E7=94=A8Arrays.asList(),=20=E5=88=99=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E5=87=8F=E5=B0=91=E5=AF=B9=E8=B1=A1=E5=88=9B=E5=BB=BA?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/compress/ZipReplacer.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java index 2e72205b0..caefb758c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java @@ -32,9 +32,9 @@ public class ZipReplacer { * @param charset 读取编码格式 * @param charsetOut 输出编码格式 */ - public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles, Charset charset, Charset charsetOut) { -// 记录zip中是否存在相同路径的文件, 是 更新文件 否,添加文件 - boolean[] updates = new boolean[replaceFiles.length]; + public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles, Charset charset, Charset charsetOut) { + // 记录zip中是否存在相同路径的文件, 是 更新文件 否,添加文件 + boolean[] updates = new boolean[replaceFiles.size()]; try (ZipFile zipFile = new ZipFile(srcFile, charset); FileOutputStream fos = new FileOutputStream(tarFile); ZipOutputStream zos = new ZipOutputStream(fos); @@ -50,7 +50,7 @@ public class ZipReplacer { // 存在同路径文件,替换 if (update) { updates[i] = true; - data.put(zipEntryInName, FileUtil.getInputStream(replaceFiles[i])); + data.put(zipEntryInName, FileUtil.getInputStream(replaceFiles.get(i))); break; } } @@ -63,7 +63,7 @@ public class ZipReplacer { for (int i = 0; i < updates.length; i++) { if (!updates[i]) { // 原zip中不存在同路径文件,添加到制定目录 - data.put(innerFiles[i], FileUtil.getInputStream(replaceFiles[i])); + data.put(innerFiles[i], FileUtil.getInputStream(replaceFiles.get(i))); } } for (String key : data.keySet()) { @@ -73,10 +73,8 @@ public class ZipReplacer { throw new IORuntimeException(e); } } - - public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles, Charset charset, Charset charsetOut) { - File[] files = replaceFiles.toArray(new File[0]); - replace(srcFile, tarFile, innerFiles, files, charset, charsetOut); + public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles, Charset charset, Charset charsetOut) { + replace(srcFile, tarFile, innerFiles, Arrays.asList(replaceFiles), charset, charsetOut); } public static void replace(File srcFile, File tarFile, String[] innerFiles, String[] replaceFiles, Charset charset, Charset charsetOut) { From 4f17d18e0581941fabc6b4cf85267db00df4a642 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 25 Jun 2023 15:58:34 +0800 Subject: [PATCH 03/26] fix code --- .../main/java/org/dromara/hutool/core/map/MapUtil.java | 10 +--------- .../java/org/dromara/hutool/core/map/MapUtilTest.java | 7 +++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java index 29f9ddd9e..0246ad9e8 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java @@ -1013,15 +1013,7 @@ public class MapUtil extends MapGetUtil { return map; } - final Iterator> iter = map.entrySet().iterator(); - Entry entry; - while (iter.hasNext()) { - entry = iter.next(); - if (null == entry.getValue()) { - iter.remove(); - } - } - + map.entrySet().removeIf(entry -> null == entry.getValue()); return map; } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java index 1d91bf2e9..e58db6ffc 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java @@ -285,4 +285,11 @@ public class MapUtilTest { final Map map = MapUtil.renameKey(v1, "name", "newName"); Assertions.assertEquals("张三", map.get("newName")); } + + @Test + public void removeNullValueTest() { + final Dict v1 = Dict.of().set("id", 12).set("name", null).set("age", null); + final Map map = MapUtil.removeNullValue(v1); + Assertions.assertEquals(1, map.size()); + } } From e205d2659abb9c231b59f5a3611a3ff51ea8a000 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 25 Jun 2023 16:40:12 +0800 Subject: [PATCH 04/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8DMapUtil=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E4=BD=BF=E7=94=A8filter=E6=96=B9=E6=B3=95=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E4=BC=A0=E5=85=A5=E5=8F=82=E6=95=B0=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/hutool/core/map/MapUtil.java | 36 +++++++++---------- .../dromara/hutool/core/map/MapUtilTest.java | 15 ++++++++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java index 0246ad9e8..bd732887a 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java @@ -253,16 +253,21 @@ public class MapUtil extends MapGetUtil { */ @SuppressWarnings("unchecked") public static Map createMap(final Class mapType, final Supplier> defaultMap) { - if (null == mapType || mapType.isAssignableFrom(AbstractMap.class)) { - return defaultMap.get(); - } else { - try { - return (Map) ConstructorUtil.newInstance(mapType); - } catch (final Exception e) { - // 不支持的map类型,返回默认的HashMap - return defaultMap.get(); - } + Map result = null; + if (null != mapType && !mapType.isAssignableFrom(AbstractMap.class)) { + result = (Map) ConstructorUtil.newInstanceIfPossible(mapType); } + + if(null == result){ + result = defaultMap.get(); + } + + if(!result.isEmpty()){ + // issue#3162@Github,在构造中put值,会导致新建map带有值内容,此处清空 + result.clear(); + } + + return result; } // ----------------------------------------------------------------------------------------------- value of @@ -657,16 +662,12 @@ public class MapUtil extends MapGetUtil { * @param editor 编辑器接口 * @return 编辑后的Map */ - @SuppressWarnings("unchecked") public static Map edit(final Map map, final UnaryOperator> editor) { if (null == map || null == editor) { return map; } - Map map2 = ConstructorUtil.newInstanceIfPossible(map.getClass()); - if (null == map2) { - map2 = new HashMap<>(map.size(), 1f); - } + final Map map2 = createMap(map.getClass(), ()-> new HashMap<>(map.size(), 1f)); if (isEmpty(map)) { return map2; } @@ -681,6 +682,8 @@ public class MapUtil extends MapGetUtil { return map2; } + + /** * 过滤
* 过滤过程通过传入的Editor实现来返回需要的元素内容,这个Filter实现可以实现以下功能: @@ -739,10 +742,7 @@ public class MapUtil extends MapGetUtil { return map; } - Map map2 = ConstructorUtil.newInstanceIfPossible(map.getClass()); - if (null == map2) { - map2 = new HashMap<>(map.size(), 1f); - } + final Map map2 = createMap(map.getClass(), ()-> new HashMap<>(map.size(), 1f)); if (isEmpty(map)) { return map2; } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java index e58db6ffc..c2726b91c 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/map/MapUtilTest.java @@ -292,4 +292,19 @@ public class MapUtilTest { final Map map = MapUtil.removeNullValue(v1); Assertions.assertEquals(1, map.size()); } + + @Test + public void issue3162Test() { + final Map map = new HashMap() { + private static final long serialVersionUID = 1L; + { + put("a", "1"); + put("b", "2"); + put("c", "3"); + }}; + final Map filtered = MapUtil.filter(map, "a", "b"); + Assertions.assertEquals(2, filtered.size()); + Assertions.assertEquals("1", filtered.get("a")); + Assertions.assertEquals("2", filtered.get("b")); + } } From 45ed3a95f9f4df570971b9b7266becdb2cc12e13 Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 25 Jun 2023 18:10:47 +0800 Subject: [PATCH 05/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E5=92=8C=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96Class?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/convert/CompositeConverter.java | 5 ++ .../core/convert/RegisterConverter.java | 51 ++----------------- .../core/convert/impl/ClassConverter.java | 5 ++ .../dromara/hutool/json/IssueI7FQ29Test.java | 35 +++++++++++++ 4 files changed, 48 insertions(+), 48 deletions(-) create mode 100755 hutool-json/src/test/java/org/dromara/hutool/json/IssueI7FQ29Test.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java index e95d3251e..cc33061a9 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java @@ -231,6 +231,11 @@ public class CompositeConverter extends RegisterConverter { return (T) KBeanConverter.INSTANCE.convert(type, value); } + // issue#I7FQ29 Class + if("java.lang.Class".equals(rowType.getName())){ + return (T) ClassConverter.INSTANCE.convert(type, value); + } + // 表示非需要特殊转换的对象 return null; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java index 1fd0a8cf3..26bb55a97 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java @@ -12,33 +12,7 @@ package org.dromara.hutool.core.convert; -import org.dromara.hutool.core.convert.impl.AtomicBooleanConverter; -import org.dromara.hutool.core.convert.impl.AtomicIntegerArrayConverter; -import org.dromara.hutool.core.convert.impl.AtomicLongArrayConverter; -import org.dromara.hutool.core.convert.impl.AtomicReferenceConverter; -import org.dromara.hutool.core.convert.impl.BooleanConverter; -import org.dromara.hutool.core.convert.impl.CalendarConverter; -import org.dromara.hutool.core.convert.impl.CharacterConverter; -import org.dromara.hutool.core.convert.impl.CharsetConverter; -import org.dromara.hutool.core.convert.impl.ClassConverter; -import org.dromara.hutool.core.convert.impl.CurrencyConverter; -import org.dromara.hutool.core.convert.impl.DateConverter; -import org.dromara.hutool.core.convert.impl.DurationConverter; -import org.dromara.hutool.core.convert.impl.LocaleConverter; -import org.dromara.hutool.core.convert.impl.OptConverter; -import org.dromara.hutool.core.convert.impl.OptionalConverter; -import org.dromara.hutool.core.convert.impl.PathConverter; -import org.dromara.hutool.core.convert.impl.PeriodConverter; -import org.dromara.hutool.core.convert.impl.ReferenceConverter; -import org.dromara.hutool.core.convert.impl.StackTraceElementConverter; -import org.dromara.hutool.core.convert.impl.StringConverter; -import org.dromara.hutool.core.convert.impl.TemporalAccessorConverter; -import org.dromara.hutool.core.convert.impl.TimeZoneConverter; -import org.dromara.hutool.core.convert.impl.URIConverter; -import org.dromara.hutool.core.convert.impl.URLConverter; -import org.dromara.hutool.core.convert.impl.UUIDConverter; -import org.dromara.hutool.core.convert.impl.XMLGregorianCalendarConverter; -import org.dromara.hutool.core.convert.impl.ZoneIdConverter; +import org.dromara.hutool.core.convert.impl.*; import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.map.SafeConcurrentHashMap; @@ -52,27 +26,9 @@ import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Path; -import java.time.DayOfWeek; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.Month; -import java.time.MonthDay; -import java.time.OffsetDateTime; -import java.time.OffsetTime; -import java.time.Period; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.time.*; import java.time.temporal.TemporalAccessor; -import java.util.Calendar; -import java.util.Currency; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.TimeZone; -import java.util.UUID; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicLongArray; @@ -248,7 +204,6 @@ public class RegisterConverter implements Converter, Serializable { defaultConverterMap.put(AtomicLongArray.class, new AtomicLongArrayConverter()); // 其它类型 - defaultConverterMap.put(Class.class, new ClassConverter()); defaultConverterMap.put(TimeZone.class, new TimeZoneConverter()); defaultConverterMap.put(ZoneId.class, new ZoneIdConverter()); defaultConverterMap.put(Locale.class, new LocaleConverter()); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java index 10c291315..abb417a84 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java @@ -24,6 +24,11 @@ import org.dromara.hutool.core.classloader.ClassLoaderUtil; public class ClassConverter extends AbstractConverter { private static final long serialVersionUID = 1L; + /** + * 单例 + */ + public static ClassConverter INSTANCE = new ClassConverter(); + private final boolean isInitialized; /** diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7FQ29Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7FQ29Test.java new file mode 100755 index 000000000..8c92949fa --- /dev/null +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7FQ29Test.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.json; + +import org.dromara.hutool.core.reflect.TypeReference; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +/** + * https://gitee.com/dromara/hutool/issues/I7FQ29 + */ +public class IssueI7FQ29Test { + + @Test + void toMapTest() { + final String jsonStr = "{\"trans_no\": \"java.lang.String\"}"; + final Map> map = JSONUtil.toBean(jsonStr, new TypeReference>>() { + }); + + Assertions.assertNotNull(map); + Assertions.assertEquals(String.class, map.get("trans_no")); + } +} From 16fc04d9551f1a04b309160e2f572034b40788d5 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 26 Jun 2023 12:34:52 +0800 Subject: [PATCH 06/26] =?UTF-8?q?ExcelReader.getWriter=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=EF=BC=8C=E5=BD=93=E4=BB=8E=E9=9D=9E=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=AF=BB=E5=8F=96=E6=97=B6=EF=BC=8C=E8=8E=B7=E5=8F=96?= =?UTF-8?q?sheet=EF=BC=8C=E8=80=8C=E9=9D=9E=E7=A9=BA=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/hutool/poi/excel/ExcelReader.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelReader.java index 106470458..b540b2b9b 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelReader.java @@ -428,13 +428,20 @@ public class ExcelReader extends ExcelBase { /** * 获取Excel写出器
- * 在读取Excel并做一定编辑后,获取写出器写出
- * 注意,只读方式下,此方法无效 + * 在读取Excel并做一定编辑后,获取写出器写出,规则如下: + *
    + *
  • 1. 当从流中读取时,转换为Writer直接使用Sheet对象,此时修改不会影响源文件,Writer中flush需要指定新的路径。
  • + *
  • 2. 当从文件读取时,直接获取文件及sheet名称,此时可以修改原文件。
  • + *
* * @return {@link ExcelWriter} * @since 4.0.6 */ public ExcelWriter getWriter() { + if (null == this.destFile) { + // 非读取文件形式,直接获取sheet操作。 + return new ExcelWriter(this.sheet); + } return ExcelUtil.getWriter(this.destFile, this.sheet.getSheetName()); } From c3380f7aeee76f5801857faa730a1ad3997a6d13 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 27 Jun 2023 10:11:26 +0800 Subject: [PATCH 07/26] fix code --- .../dromara/hutool/setting/SettingLoader.java | 6 ++-- .../hutool/setting/IssueI7G34ETest.java | 28 +++++++++++++++++++ .../src/test/resources/test_with_bom.setting | 3 ++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100755 hutool-setting/src/test/java/org/dromara/hutool/setting/IssueI7G34ETest.java create mode 100755 hutool-setting/src/test/resources/test_with_bom.setting diff --git a/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java b/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java index 78b1ceae7..09e6a65fc 100644 --- a/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java +++ b/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java @@ -125,7 +125,7 @@ public class SettingLoader { if (line == null) { break; } - line = line.trim(); + line = StrUtil.trim(line); // 跳过注释行和空行 if (StrUtil.isBlank(line) || StrUtil.startWith(line, COMMENT_FLAG_PRE)) { continue; @@ -133,7 +133,7 @@ public class SettingLoader { // 记录分组名 if (StrUtil.isWrap(line, CharUtil.BRACKET_START, CharUtil.BRACKET_END)) { - group = line.substring(1, line.length() - 1).trim(); + group = StrUtil.trim(line.substring(1, line.length() - 1)); continue; } @@ -149,7 +149,7 @@ public class SettingLoader { if (this.isUseVariable) { value = replaceVar(group, value); } - this.groupedMap.put(group, keyValue[0].trim(), value); + this.groupedMap.put(group, StrUtil.trim(keyValue[0]), value); } } finally { IoUtil.closeQuietly(reader); diff --git a/hutool-setting/src/test/java/org/dromara/hutool/setting/IssueI7G34ETest.java b/hutool-setting/src/test/java/org/dromara/hutool/setting/IssueI7G34ETest.java new file mode 100755 index 000000000..07883812b --- /dev/null +++ b/hutool-setting/src/test/java/org/dromara/hutool/setting/IssueI7G34ETest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.setting; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IssueI7G34ETest { + + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + @Test + void readWithBomTest() { + final Setting setting = new Setting("test_with_bom.setting"); + final String s = setting.getStrByGroup("key1", "line1"); + + Assertions.assertEquals("value1", s); + } +} diff --git a/hutool-setting/src/test/resources/test_with_bom.setting b/hutool-setting/src/test/resources/test_with_bom.setting new file mode 100755 index 000000000..32e92aa16 --- /dev/null +++ b/hutool-setting/src/test/resources/test_with_bom.setting @@ -0,0 +1,3 @@ +[line1] +key1 = value1 +key2 = value2 From 66f1d507049370e6f449013845cce1134a1feda3 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 27 Jun 2023 12:11:57 +0800 Subject: [PATCH 08/26] add ZipReplacer --- .../hutool/core/compress/ZipReplacer.java | 317 ++++-------------- .../org/dromara/hutool/core/net/Ipv4Util.java | 31 +- .../dromara/hutool/core/net/Ipv4UtilTest.java | 9 + 3 files changed, 108 insertions(+), 249 deletions(-) mode change 100644 => 100755 hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java old mode 100644 new mode 100755 index caefb758c..f5e9256e2 --- a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipReplacer.java @@ -1,247 +1,84 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + package org.dromara.hutool.core.compress; -import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.io.resource.Resource; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.util.CharsetUtil; -import java.io.File; -import java.io.FileOutputStream; +import java.io.Closeable; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Enumeration; import java.util.HashMap; -import java.util.List; -import java.util.regex.Matcher; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; +import java.util.Map; -public class ZipReplacer { +/** + * Zip文件替换,用户替换源Zip文件,并生成新的文件 + * + * @author looly + * @since 6.0.0 + */ +public class ZipReplacer implements Closeable { + + private final ZipReader zipReader; + private final boolean ignoreCase; + + private final Map replacedResources = new HashMap<>(); /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 + * 构造 * - * @param srcFile 源文件 - * @param tarFile 输出文件 - * @param innerFiles 替换文件在zip中的相对路径 - * @param replaceFiles 替换文件到系统中到存储路径 - * @param charset 读取编码格式 - * @param charsetOut 输出编码格式 + * @param zipReader ZipReader + * @param ignoreCase 是否忽略path大小写 */ - public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles, Charset charset, Charset charsetOut) { - // 记录zip中是否存在相同路径的文件, 是 更新文件 否,添加文件 - boolean[] updates = new boolean[replaceFiles.size()]; - try (ZipFile zipFile = new ZipFile(srcFile, charset); - FileOutputStream fos = new FileOutputStream(tarFile); - ZipOutputStream zos = new ZipOutputStream(fos); - ZipWriter zipWriter = new ZipWriter(zos, charsetOut)) { - HashMap data = new HashMap<>(); - for (Enumeration entries = zipFile.entries(); entries.hasMoreElements(); ) { - ZipEntry zipEntryIn = entries.nextElement(); - String zipEntryInName = zipEntryIn.getName(); -// false 未被替换 - boolean update = false; - for (int i = 0; i < innerFiles.length; i++) { - update = samePath(zipEntryInName, innerFiles[i]); -// 存在同路径文件,替换 - if (update) { - updates[i] = true; - data.put(zipEntryInName, FileUtil.getInputStream(replaceFiles.get(i))); - break; - } - } -// 不存在同路径文件,直接添加原zipEntry - if (!update) { - data.put(zipEntryInName, ZipUtil.getStream(zipFile, zipEntryIn)); + public ZipReplacer(final ZipReader zipReader, final boolean ignoreCase) { + this.zipReader = zipReader; + this.ignoreCase = ignoreCase; + } + + /** + * 增加替换的内容,如果路径不匹配,则不做替换,也不加入 + * + * @param entryPath 路径 + * @param resource 被压缩的内容 + * @return this + */ + public ZipReplacer addReplace(final String entryPath, final Resource resource) { + replacedResources.put(entryPath, resource); + return this; + } + + /** + * 写出到{@link ZipWriter} + * + * @param writer {@link ZipWriter} + */ + public void write(final ZipWriter writer) { + zipReader.read((entry) -> { + String entryName; + for (final String key : replacedResources.keySet()) { + entryName = entry.getName(); + if (isSamePath(entryName, key, ignoreCase)) { + writer.add(key, replacedResources.get(key).getStream()); + } else { + writer.add(entryName, zipReader.get(entryName)); } } -// 确认replaceFiles是否替换原zip中文件,没有替换直接添加 - for (int i = 0; i < updates.length; i++) { - if (!updates[i]) { -// 原zip中不存在同路径文件,添加到制定目录 - data.put(innerFiles[i], FileUtil.getInputStream(replaceFiles.get(i))); - } - } - for (String key : data.keySet()) { - zipWriter.add(key, data.get(key)); - } - } catch (IOException e) { - throw new IORuntimeException(e); - } - } - public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles, Charset charset, Charset charsetOut) { - replace(srcFile, tarFile, innerFiles, Arrays.asList(replaceFiles), charset, charsetOut); + }); } - public static void replace(File srcFile, File tarFile, String[] innerFiles, String[] replaceFiles, Charset charset, Charset charsetOut) { - List files = Arrays.stream(replaceFiles).map(FileUtil::file).collect(Collectors.toList()); - replace(srcFile, tarFile, innerFiles, files, charset, charsetOut); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFile 源文件 - * @param tarFile 输出文件 - * @param innerFiles 替换文件在zip中的相对路径 - * @param replaceFiles 替换文件到系统中到存储路径 - * @param charset 读取输出编码格式 - */ - public static void replace(File srcFile, File tarFile, String[] innerFiles, String[] replaceFiles, Charset charset) { - replace(srcFile, tarFile, innerFiles, replaceFiles, charset, charset); - } - - public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles, Charset charset) { - replace(srcFile, tarFile, innerFiles, replaceFiles, charset, charset); - } - - public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles, Charset charset) { - replace(srcFile, tarFile, innerFiles, replaceFiles, charset, charset); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFile 源文件 - * @param tarFile 输出文件 - * @param innerFiles 要替换文件在zip中的相对路径 - * @param replaceFiles 替换文件到系统中到存储路径 - */ - public static void replace(File srcFile, File tarFile, String[] innerFiles, String[] replaceFiles) { - replace(srcFile, tarFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(File srcFile, File tarFile, String[] innerFiles, File[] replaceFiles) { - replace(srcFile, tarFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(File srcFile, File tarFile, String[] innerFiles, List replaceFiles) { - replace(srcFile, tarFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFile 源文件,也是输出路径 - * @param innerFiles 替换文件在zip中的相对路径 - * @param replaceFiles 替换文件到系统中到存储路径 - * @param charset 读取输出编码格式 - */ - public static void replace(File srcFile, String[] innerFiles, String[] replaceFiles, Charset charset) { - replace(srcFile, srcFile, innerFiles, replaceFiles, charset); - } - - public static void replace(File srcFile, String[] innerFiles, File[] replaceFiles, Charset charset) { - replace(srcFile, srcFile, innerFiles, replaceFiles, charset); - } - - public static void replace(File srcFile, String[] innerFiles, List replaceFiles, Charset charset) { - replace(srcFile, srcFile, innerFiles, replaceFiles, charset); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFile 源文件 - * @param innerFiles 要替换文件在zip中的相对路径 - * @param replaceFiles 要替换的文件绝对路径 - */ - public static void replace(File srcFile, String[] innerFiles, String[] replaceFiles) { - replace(srcFile, srcFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(File srcFile, String[] innerFiles, File[] replaceFiles) { - replace(srcFile, srcFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(File srcFile, String[] innerFiles, List replaceFiles) { - replace(srcFile, srcFile, innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFilePath 源文件路径 - * @param tarFilePath 输出文件路径 - * @param innerFiles 要替换文件在zip中的相对路径 - * @param replaceFiles 要替换的文件绝对路径 - * @param charset 读取编码格式 - * @param charsetOut 输出编码格式 - */ - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, String[] replaceFiles, Charset charset, Charset charsetOut) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charsetOut); - } - - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, File[] replaceFiles, Charset charset, Charset charsetOut) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charsetOut); - } - - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, List replaceFiles, Charset charset, Charset charsetOut) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charsetOut); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFilePath zip源文件路径 - * @param tarFilePath zip输出文件路径 - * @param innerFiles 要替换文件在zip中的相对路径 - * @param replaceFiles 要替换的文件绝对路径 - * @param charset 读取输出编码格式 - */ - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, String[] replaceFiles, Charset charset) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charset); - } - - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, File[] replaceFiles, Charset charset) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charset); - } - - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, List replaceFiles, Charset charset) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, charset, charset); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFilePath 源文件路径 - * @param tarFilePath 输出文件路径 - * @param innerFiles 替换文件在zip中的相对路径 - * @param replaceFiles 替换文件到系统中到存储路径 - */ - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, String[] replaceFiles) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, File[] replaceFiles) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(String srcFilePath, String tarFilePath, String[] innerFiles, List replaceFiles) { - replace(FileUtil.file(srcFilePath), FileUtil.file(tarFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - /** - * 不解压zip,替换文件内容,也可用于转换zip编码格式 - * - * @param srcFilePath 源文件路径,也是输出路径 - * @param innerFiles 要替换文件在zip中的相对路径 - * @param replaceFiles 要替换的文件绝对路径 - */ - public static void replace(String srcFilePath, String[] innerFiles, String[] replaceFiles) { - replace(FileUtil.file(srcFilePath), FileUtil.file(srcFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(String srcFilePath, String[] innerFiles, File[] replaceFiles) { - replace(FileUtil.file(srcFilePath), FileUtil.file(srcFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); - } - - public static void replace(String srcFilePath, String[] innerFiles, List replaceFiles) { - replace(FileUtil.file(srcFilePath), FileUtil.file(srcFilePath), innerFiles, replaceFiles, CharsetUtil.defaultCharset()); + @Override + public void close() throws IOException { + this.zipReader.close(); } /** @@ -252,25 +89,9 @@ public class ZipReplacer { * @param ignoreCase 是否忽略大小写 * @return ture 路径相等 */ - public static boolean samePath(String entryPath, String targetPath, boolean ignoreCase) { - - entryPath = entryPath.replaceAll("[/\\\\]", Matcher.quoteReplacement(File.separator)); - targetPath = targetPath.replaceAll("[/\\\\]", Matcher.quoteReplacement(File.separator)); - if (entryPath.startsWith(File.separator)) { - entryPath = entryPath.substring(1); - } - if (targetPath.startsWith(File.separator)) { - targetPath = targetPath.substring(1); - } - - if (ignoreCase) { - return StrUtil.equalsIgnoreCase(entryPath, targetPath); - } else { - return StrUtil.equals(entryPath, targetPath); - } - } - - public static boolean samePath(String entryPath, String targetPath) { - return samePath(entryPath, targetPath, true); + private static boolean isSamePath(String entryPath, String targetPath, final boolean ignoreCase) { + entryPath = StrUtil.removePrefix(FileUtil.normalize(entryPath), StrUtil.SLASH); + targetPath = StrUtil.removePrefix(FileUtil.normalize(targetPath), StrUtil.SLASH); + return StrUtil.equals(entryPath, targetPath, ignoreCase); } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/net/Ipv4Util.java b/hutool-core/src/main/java/org/dromara/hutool/core/net/Ipv4Util.java index 07ba4cf78..17f2d640c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/net/Ipv4Util.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/net/Ipv4Util.java @@ -17,9 +17,10 @@ import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.lang.Singleton; import org.dromara.hutool.core.regex.PatternPool; +import org.dromara.hutool.core.regex.ReUtil; +import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.split.SplitUtil; -import org.dromara.hutool.core.text.CharUtil; import java.net.Inet4Address; import java.net.InetAddress; @@ -567,6 +568,34 @@ public class Ipv4Util implements Ipv4Pool { throw new IllegalArgumentException("Illegal position of ip Long: " + position); } } + + /** + * 检测指定 IP 地址是否匹配通配符 wildcard + * + * @param wildcard 通配符,如 192.168.*.1 + * @param ipAddress 待检测的 IP 地址 + * @return 是否匹配 + */ + public static boolean matches(final String wildcard, final String ipAddress) { + if (!ReUtil.isMatch(PatternPool.IPV4, ipAddress)) { + return false; + } + + final String[] wildcardSegments = SplitUtil.splitToArray(wildcard, StrUtil.DOT); + final String[] ipSegments = SplitUtil.splitToArray(ipAddress, StrUtil.DOT); + + if (wildcardSegments.length != ipSegments.length) { + return false; + } + + for (int i = 0; i < wildcardSegments.length; i++) { + if (!"*".equals(wildcardSegments[i]) && !wildcardSegments[i].equals(ipSegments[i])) { + return false; + } + } + return true; + } + //-------------------------------------------------------------------------------- Private method start /** diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/net/Ipv4UtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/net/Ipv4UtilTest.java index 2af24db1c..42ac19ca6 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/net/Ipv4UtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/net/Ipv4UtilTest.java @@ -229,4 +229,13 @@ public class Ipv4UtilTest { final boolean match = ReUtil.isMatch(PatternPool.MAC_ADDRESS, macAddress); Assertions.assertTrue(match); } + + @Test + public void matchesTest() { + final boolean matches1 = Ipv4Util.matches("127.*.*.1", "127.0.0.1"); + Assertions.assertTrue(matches1); + + final boolean matches2 = Ipv4Util.matches("192.168.*.1", "127.0.0.1"); + Assertions.assertFalse(matches2); + } } From 8c50bc829cf9fb16ea16fa1638dd19139b1eadca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=95=A2=E6=95=A2?= Date: Tue, 27 Jun 2023 15:42:33 +0800 Subject: [PATCH 09/26] =?UTF-8?q?=E4=BF=AE=E6=94=B9CollUtil.union=E7=AD=89?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=B3=9B=E5=9E=8B=EF=BC=8C=E4=BD=BF=E5=85=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D=E5=AD=90=E7=B1=BBunion?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E7=88=B6=E7=B1=BB=E9=9B=86=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/collection/CollUtil.java | 6 +-- .../core/collection/CollectionOperation.java | 7 +-- .../hutool/core/collection/CollUtilTest.java | 43 +++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java index 39a1e444d..fd067d2bd 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollUtil.java @@ -297,7 +297,7 @@ public class CollUtil { * @return 并集的集合,返回 {@link ArrayList} */ @SafeVarargs - public static Collection union(final Collection... colls) { + public static Collection union(final Collection... colls) { return CollectionOperation.of(colls).union(); } @@ -312,7 +312,7 @@ public class CollUtil { * @return 并集的集合,返回 {@link LinkedHashSet} */ @SafeVarargs - public static Set unionDistinct(final Collection... colls) { + public static Set unionDistinct(final Collection... colls) { return CollectionOperation.of(colls).unionDistinct(); } @@ -327,7 +327,7 @@ public class CollUtil { * @return 并集的集合,返回 {@link ArrayList} */ @SafeVarargs - public static List unionAll(final Collection... colls) { + public static List unionAll(final Collection... colls) { return CollectionOperation.of(colls).unionAll(); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollectionOperation.java b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollectionOperation.java index bb700e0f4..6e3faa75c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollectionOperation.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/collection/CollectionOperation.java @@ -45,7 +45,7 @@ public class CollectionOperation { * @return CollectionOperation */ @SafeVarargs - public static CollectionOperation of(final Collection... colls) { + public static CollectionOperation of(final Collection... colls) { return new CollectionOperation<>(colls); } @@ -56,8 +56,9 @@ public class CollectionOperation { * * @param colls 集合数组 */ - public CollectionOperation(final Collection[] colls) { - this.colls = colls; + @SuppressWarnings("unchecked") + public CollectionOperation(final Collection[] colls) { + this.colls = (Collection[]) colls; } /** diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java index 8aebcef74..5e0a58bfe 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java @@ -1110,6 +1110,26 @@ public class CollUtilTest { } } + @ToString(callSuper = true) + @EqualsAndHashCode(callSuper = true) + @Data + static class Cat extends Animal { + + public Cat(String name, Integer age) { + super(name, age); + } + } + + @ToString(callSuper = true) + @EqualsAndHashCode(callSuper = true) + @Data + static class Pig extends Animal { + + public Pig(String name, Integer age) { + super(name, age); + } + } + @Test public void getFirstTest() { Assertions.assertNull(CollUtil.getFirst(null)); @@ -1181,4 +1201,27 @@ public class CollUtilTest { public void minNullTest() { Assertions.assertNull(CollUtil.max(null)); } + + @Test + public void unionExtendTest() { + List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12)); + List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); + Assertions.assertEquals(CollUtil.union(dog, cat).size(), dog.size() + cat.size()); + } + + @Test + public void unionAllExtendTest() { + List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12)); + List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); + List pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12)); + Assertions.assertEquals(CollUtil.unionAll(dog, cat, pig).size(), dog.size() + cat.size() + pig.size()); + } + + @Test + public void unionDistinctExtendTest() { + List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog1", 12)); // same + List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); + List pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12)); + Assertions.assertEquals(CollUtil.unionDistinct(dog, cat, pig).size(), 5); + } } From 45e22a21d8af8857c87477eb3458fa114a52fe93 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 27 Jun 2023 17:13:32 +0800 Subject: [PATCH 10/26] =?UTF-8?q?=E4=BF=AE=E6=94=B9CollUtil.union=E7=AD=89?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=B3=9B=E5=9E=8B=EF=BC=8C=E4=BD=BF=E5=85=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D=E5=AD=90=E7=B1=BBunion?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E7=88=B6=E7=B1=BB=E9=9B=86=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/collection/CollUtilTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java index 5e0a58bfe..bafe98d16 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/collection/CollUtilTest.java @@ -1115,7 +1115,7 @@ public class CollUtilTest { @Data static class Cat extends Animal { - public Cat(String name, Integer age) { + public Cat(final String name, final Integer age) { super(name, age); } } @@ -1125,7 +1125,7 @@ public class CollUtilTest { @Data static class Pig extends Animal { - public Pig(String name, Integer age) { + public Pig(final String name, final Integer age) { super(name, age); } } @@ -1204,24 +1204,24 @@ public class CollUtilTest { @Test public void unionExtendTest() { - List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12)); - List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); + final List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12)); + final List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); Assertions.assertEquals(CollUtil.union(dog, cat).size(), dog.size() + cat.size()); } @Test public void unionAllExtendTest() { - List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12)); - List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); - List pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12)); + final List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog2", 12)); + final List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); + final List pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12)); Assertions.assertEquals(CollUtil.unionAll(dog, cat, pig).size(), dog.size() + cat.size() + pig.size()); } @Test public void unionDistinctExtendTest() { - List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog1", 12)); // same - List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); - List pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12)); + final List dog = Arrays.asList(new Dog("dog1", 12), new Dog("dog1", 12)); // same + final List cat = Arrays.asList(new Cat("cat1", 12), new Cat("cat2", 12)); + final List pig = Arrays.asList(new Pig("pig1", 12), new Pig("pig2", 12)); Assertions.assertEquals(CollUtil.unionDistinct(dog, cat, pig).size(), 5); } } From edc08cf1de1e3af32d2ae58dd27fb17d09d345cd Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 28 Jun 2023 10:52:02 +0800 Subject: [PATCH 11/26] fix code --- .../src/main/java/org/dromara/hutool/core/math/NumberUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java index 51cc720ea..ec3881378 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java @@ -34,7 +34,7 @@ import java.util.Objects; * JDK7中BigDecimal(double val)构造方法的结果有一定的不可预知性,例如: * *
- * new BigDecimal(0.1)
+ * new BigDecimal(0.1)和 BigDecimal.valueOf(0.1)
  * 
*

* 表示的不是0.1而是0.1000000000000000055511151231257827021181583404541015625 From e8adf37407e69b4f71231d83898107ef6392253d Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 28 Jun 2023 16:22:50 +0800 Subject: [PATCH 12/26] fix code --- .../hutool/core/thread/ExecutorBuilder.java | 41 ++++++----- .../hutool/core/thread/ThreadUtil.java | 68 +++++++++---------- .../hutool/core/thread/Issue3167Test.java | 31 +++++++++ 3 files changed, 86 insertions(+), 54 deletions(-) create mode 100755 hutool-core/src/test/java/org/dromara/hutool/core/thread/Issue3167Test.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ExecutorBuilder.java b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ExecutorBuilder.java index d32e68b4e..48ab387cb 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ExecutorBuilder.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ExecutorBuilder.java @@ -15,16 +15,7 @@ package org.dromara.hutool.core.thread; import org.dromara.hutool.core.lang.builder.Builder; import org.dromara.hutool.core.util.ObjUtil; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.RejectedExecutionHandler; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * {@link ThreadPoolExecutor} 建造者 @@ -42,8 +33,10 @@ import java.util.concurrent.TimeUnit; public class ExecutorBuilder implements Builder { private static final long serialVersionUID = 1L; - /** 默认的等待队列容量 */ - public static final int DEFAULT_QUEUE_CAPACITY = 1024; + /** + * 默认的等待队列容量 + */ + public static final int DEFAULT_QUEUE_CAPACITY = Integer.MAX_VALUE; /** * 初始池大小 @@ -137,6 +130,18 @@ public class ExecutorBuilder implements Builder { return this; } + /** + * 使用{@link LinkedBlockingQueue} 作为等待队列
+ * 队列满时,运行线程小于maxPoolSize时会创建新线程,否则触发异常策略 + * + * @param capacity 队列容量 + * @return this + * @since 6.0.0 + */ + public ExecutorBuilder useLinkedBlockingQueue(final int capacity) { + return setWorkQueue(new LinkedBlockingQueue<>(capacity)); + } + /** * 使用{@link ArrayBlockingQueue} 做为等待队列
* 有界队列,相对无界队列有利于控制队列大小,队列满时,运行线程小于maxPoolSize时会创建新线程,否则触发异常策略 @@ -257,12 +262,12 @@ public class ExecutorBuilder implements Builder { final RejectedExecutionHandler handler = ObjUtil.defaultIfNull(builder.handler, RejectPolicy.ABORT.getValue()); final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(// - corePoolSize, // - maxPoolSize, // - keepAliveTime, TimeUnit.NANOSECONDS, // - workQueue, // - threadFactory, // - handler// + corePoolSize, // + maxPoolSize, // + keepAliveTime, TimeUnit.NANOSECONDS, // + workQueue, // + threadFactory, // + handler// ); if (null != builder.allowCoreThreadTimeOut) { threadPoolExecutor.allowCoreThreadTimeOut(builder.allowCoreThreadTimeOut); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ThreadUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ThreadUtil.java index e61577bdd..ac529e2f9 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ThreadUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ThreadUtil.java @@ -39,25 +39,6 @@ import java.util.function.Supplier; */ public class ThreadUtil { - /** - * 新建一个线程池,默认的策略如下: - *

-	 *    1. 初始线程数为corePoolSize指定的大小
-	 *    2. 没有最大线程数限制
-	 *    3. 默认使用LinkedBlockingQueue,默认队列大小为1024
-	 * 
- * - * @param corePoolSize 同时执行的线程数大小 - * @return ExecutorService - */ - public static ExecutorService newExecutor(final int corePoolSize) { - final ExecutorBuilder builder = ExecutorBuilder.of(); - if (corePoolSize > 0) { - builder.setCorePoolSize(corePoolSize); - } - return builder.build(); - } - /** * 获得一个新的线程池,默认的策略如下: *
@@ -86,10 +67,25 @@ public class ThreadUtil {
 	 */
 	public static ExecutorService newSingleExecutor() {
 		return ExecutorBuilder.of()//
-				.setCorePoolSize(1)//
-				.setMaxPoolSize(1)//
-				.setKeepAliveTime(0)//
-				.buildFinalizable();
+			.setCorePoolSize(1)//
+			.setMaxPoolSize(1)//
+			.setKeepAliveTime(0)//
+			.buildFinalizable();
+	}
+
+	/**
+	 * 新建一个线程池,默认的策略如下:
+	 * 
+	 *    1. 初始线程数为poolSize指定的大小
+	 *    2. 最大线程数为poolSize指定的大小
+	 *    3. 默认使用LinkedBlockingQueue,默认无界队列
+	 * 
+ * + * @param poolSize 同时执行的线程数大小 + * @return ExecutorService + */ + public static ExecutorService newExecutor(final int poolSize) { + return newExecutor(poolSize, poolSize); } /** @@ -102,9 +98,9 @@ public class ThreadUtil { */ public static ThreadPoolExecutor newExecutor(final int corePoolSize, final int maximumPoolSize) { return ExecutorBuilder.of() - .setCorePoolSize(corePoolSize) - .setMaxPoolSize(maximumPoolSize) - .build(); + .setCorePoolSize(corePoolSize) + .setMaxPoolSize(maximumPoolSize) + .build(); } /** @@ -119,10 +115,10 @@ public class ThreadUtil { */ public static ExecutorService newExecutor(final int corePoolSize, final int maximumPoolSize, final int maximumQueueSize) { return ExecutorBuilder.of() - .setCorePoolSize(corePoolSize) - .setMaxPoolSize(maximumPoolSize) - .setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize)) - .build(); + .setCorePoolSize(corePoolSize) + .setMaxPoolSize(maximumPoolSize) + .useLinkedBlockingQueue(maximumQueueSize) + .build(); } /** @@ -184,7 +180,7 @@ public class ThreadUtil { */ public static ExecutorService newFixedExecutor(final int nThreads, final int maximumQueueSize, final String threadNamePrefix, final boolean isBlocked) { return newFixedExecutor(nThreads, maximumQueueSize, threadNamePrefix, - (isBlocked ? RejectPolicy.BLOCK : RejectPolicy.ABORT).getValue()); + (isBlocked ? RejectPolicy.BLOCK : RejectPolicy.ABORT).getValue()); } /** @@ -207,11 +203,11 @@ public class ThreadUtil { final String threadNamePrefix, final RejectedExecutionHandler handler) { return ExecutorBuilder.of() - .setCorePoolSize(nThreads).setMaxPoolSize(nThreads) - .setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize)) - .setThreadFactory(createThreadFactory(threadNamePrefix)) - .setHandler(handler) - .build(); + .setCorePoolSize(nThreads).setMaxPoolSize(nThreads) + .setWorkQueue(new LinkedBlockingQueue<>(maximumQueueSize)) + .setThreadFactory(createThreadFactory(threadNamePrefix)) + .setHandler(handler) + .build(); } /** diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/thread/Issue3167Test.java b/hutool-core/src/test/java/org/dromara/hutool/core/thread/Issue3167Test.java new file mode 100755 index 000000000..a29de963d --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/thread/Issue3167Test.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.thread; + +import org.dromara.hutool.core.lang.Console; + +import java.util.concurrent.ExecutorService; + +public class Issue3167Test { + public static void main(final String[] args) { + final ExecutorService executorService = ThreadUtil.newExecutor(2); + + for (int i = 0; i < 1035; i++) { + final int finalI = i; + executorService.submit(() -> { + Console.log(Thread.currentThread().getName(), finalI); + ThreadUtil.sleep(5000); + }); + } + } +} From 824bf11d05ce6a27f6c08990254f1f420f03f202 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 29 Jun 2023 09:23:49 +0800 Subject: [PATCH 13/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8DPathUtil.getMimeType?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E9=80=A0=E6=88=90=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/dromara/hutool/core/io/file/PathUtil.java | 5 +++-- .../java/org/dromara/hutool/core/io/file/PathUtilTest.java | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java index fa113412f..065ea407c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/PathUtil.java @@ -594,8 +594,9 @@ public class PathUtil { public static String getMimeType(final Path file) { try { return Files.probeContentType(file); - } catch (final IOException e) { - throw new IORuntimeException(e); + } catch (final IOException ignore) { + // issue#3179,使用OpenJDK可能抛出NoSuchFileException,此处返回null + return null; } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/file/PathUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/file/PathUtilTest.java index 6aefc7dbc..58ddec385 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/io/file/PathUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/io/file/PathUtilTest.java @@ -93,4 +93,10 @@ public class PathUtilTest { public void moveTest2(){ PathUtil.move(Paths.get("D:\\project\\test1.txt"), Paths.get("D:\\project\\test2.txt"), false); } + + @Test + public void issue3179Test() { + final String mimeType = PathUtil.getMimeType(Paths.get("xxxx.jpg")); + Assertions.assertEquals("image/jpeg", mimeType); + } } From 05617ff3567b5d574c5bc4ac8a52c88640a911e8 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 29 Jun 2023 10:26:36 +0800 Subject: [PATCH 14/26] fix code --- .../hutool/core/text/CharSequenceUtil.java | 45 +++++++++++++++++++ .../hutool/json/convert/JSONConverter.java | 4 +- .../org/dromara/hutool/json/JSONUtilTest.java | 7 +++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java index 37ff073c9..ab5d317dd 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java @@ -32,7 +32,11 @@ import org.dromara.hutool.core.util.ByteUtil; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.ObjUtil; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CodingErrorAction; import java.text.MessageFormat; import java.text.Normalizer; import java.util.HashSet; @@ -3183,6 +3187,47 @@ public class CharSequenceUtil extends StrValidator { } return sub(string, 0, length) + "..."; } + + /** + * 截断字符串,使用其按照指定编码为字节后不超过maxBytes长度 + * + * @param str 原始字符串 + * @param charset 指定编码 + * @param maxBytesLength 最大字节数 + * @param factor 速算因子,取该编码下单个字符的最大可能字节数 + * @param appendDots 截断后是否追加省略号(...) + * @return 限制后的长度 + */ + public static String limitByteLength(final String str, final Charset charset, final int maxBytesLength, + final int factor, final boolean appendDots) { + //字符数*速算因子<=最大字节数 + if (str == null || str.length() * factor <= maxBytesLength) { + return str; + } + final byte[] sba = str.getBytes(charset); + if (sba.length <= maxBytesLength) { + return str; + } + //限制字节数 + final int limitBytes; + if (appendDots) { + limitBytes = maxBytesLength - "...".getBytes(charset).length; + } else { + limitBytes = maxBytesLength; + } + final ByteBuffer bb = ByteBuffer.wrap(sba, 0, limitBytes); + final CharBuffer cb = CharBuffer.allocate(limitBytes); + final CharsetDecoder decoder = charset.newDecoder(); + //忽略被截断的字符 + decoder.onMalformedInput(CodingErrorAction.IGNORE); + decoder.decode(bb, cb, true); + decoder.flush(cb); + final String result = new String(cb.array(), 0, cb.position()); + if (appendDots) { + return result + "..."; + } + return result; + } // endregion // region ----- firstXXX diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java index 44e8a0866..ca407f0af 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java @@ -124,7 +124,7 @@ public class JSONConverter implements Converter { *
  • String: 转换为相应的对象,"和'包围的字符串返回原字符串,""返回{@code null}
  • *
  • Array、Iterable、Iterator:转换为JSONArray
  • *
  • Bean对象:转为JSONObject
  • - *
  • Number:返回原对象
  • + *
  • Number、Boolean:返回原对象
  • *
  • null:返回{@code null}
  • * * @@ -138,7 +138,7 @@ public class JSONConverter implements Converter { return null; } final JSON json; - if (obj instanceof JSON || obj instanceof Number) { + if (obj instanceof JSON || obj instanceof Number || obj instanceof Boolean) { return obj; } else if (obj instanceof CharSequence) { final String jsonStr = StrUtil.trim((CharSequence) obj); diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java index 3ef1a19d5..33c1a00a4 100644 --- a/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java @@ -2,6 +2,7 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.date.DateUtil; +import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.math.NumberUtil; import org.dromara.hutool.json.serialize.JSONStringer; @@ -334,4 +335,10 @@ public class JSONUtilTest { private Byte[] d = new Byte[0]; private Byte[] e = new Byte[1]; } + + @Test + void toJsonStrOfBooleanTest() { + final String jsonStr = JSONUtil.toJsonStr(true); + Assertions.assertEquals("true", jsonStr); + } } From a172440412580681986471dfd111920cf19398c9 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 29 Jun 2023 16:27:35 +0800 Subject: [PATCH 15/26] add test --- .../dromara/hutool/db/IssueI7GUKOTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100755 hutool-db/src/test/java/org/dromara/hutool/db/IssueI7GUKOTest.java diff --git a/hutool-db/src/test/java/org/dromara/hutool/db/IssueI7GUKOTest.java b/hutool-db/src/test/java/org/dromara/hutool/db/IssueI7GUKOTest.java new file mode 100755 index 000000000..b7a84988b --- /dev/null +++ b/hutool-db/src/test/java/org/dromara/hutool/db/IssueI7GUKOTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.db; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IssueI7GUKOTest { + @Test + void entityTest() { + final Entity entity = Entity.of("data") + .set("sort", "test") + .set("pcode", "test") + .set("code", "test") + .set("define", "test") + .set("icon", "test") + .set("label", "test") + .set("stu", "test"); + + Assertions.assertEquals("[sort, pcode, code, define, icon, label, stu]", entity.keySet().toString()); + } +} From 429c12135ef87d532a524a1cd7113a3f7790e49d Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 29 Jun 2023 16:52:09 +0800 Subject: [PATCH 16/26] fix code --- .../org/dromara/hutool/core/io/file/FileTypeUtil.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileTypeUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileTypeUtil.java index 8c2641c0a..268b21958 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileTypeUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/file/FileTypeUtil.java @@ -90,7 +90,7 @@ public class FileTypeUtil { /** * 根据文件流的头部信息获得文件类型
    * 注意此方法会读取头部一些bytes,造成此流接下来读取时缺少部分bytes
    - * 因此如果想服用此流,流需支持{@link InputStream#reset()}方法。 + * 因此如果想复用此流,流需支持{@link InputStream#reset()}方法。 * @param in {@link InputStream} * @param isExact 是否精确匹配,如果为false,使用前64个bytes匹配,如果为true,使用前8192bytes匹配 * @return 类型,文件的扩展名,未找到为{@code null} @@ -105,7 +105,7 @@ public class FileTypeUtil { /** * 根据文件流的头部信息获得文件类型
    * 注意此方法会读取头部64个bytes,造成此流接下来读取时缺少部分bytes
    - * 因此如果想服用此流,流需支持{@link InputStream#reset()}方法。 + * 因此如果想复用此流,流需支持{@link InputStream#reset()}方法。 * @param in {@link InputStream} * @return 类型,文件的扩展名,未找到为{@code null} * @throws IORuntimeException 读取流引起的异常 @@ -117,7 +117,7 @@ public class FileTypeUtil { /** * 根据文件流的头部信息获得文件类型 * 注意此方法会读取头部64个bytes,造成此流接下来读取时缺少部分bytes
    - * 因此如果想服用此流,流需支持{@link InputStream#reset()}方法。 + * 因此如果想复用此流,流需支持{@link InputStream#reset()}方法。 * *
     	 *     1、无法识别类型默认按照扩展名识别
    @@ -137,7 +137,7 @@ public class FileTypeUtil {
     	/**
     	 * 根据文件流的头部信息获得文件类型
     	 * 注意此方法会读取头部一些bytes,造成此流接下来读取时缺少部分bytes
    - * 因此如果想服用此流,流需支持{@link InputStream#reset()}方法。 + * 因此如果想复用此流,流需支持{@link InputStream#reset()}方法。 * *
     	 *     1、无法识别类型默认按照扩展名识别
    
    From 266546961241dd37992ab873674aeea5c2ae0489 Mon Sep 17 00:00:00 2001
    From: Looly 
    Date: Thu, 29 Jun 2023 17:02:08 +0800
    Subject: [PATCH 17/26] fix code
    
    ---
     .../hutool/core/date/format/parser/ISO8601DateParser.java  | 6 ++++--
     .../java/org/dromara/hutool/core/date/DateUtilTest.java    | 7 +++++++
     2 files changed, 11 insertions(+), 2 deletions(-)
    
    diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java
    index ddf8e9de5..9caf366c2 100644
    --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java
    +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java
    @@ -16,6 +16,7 @@ import org.dromara.hutool.core.date.DateException;
     import org.dromara.hutool.core.date.DatePattern;
     import org.dromara.hutool.core.date.DateTime;
     import org.dromara.hutool.core.date.format.DefaultDateBasic;
    +import org.dromara.hutool.core.lang.Console;
     import org.dromara.hutool.core.regex.ReUtil;
     import org.dromara.hutool.core.text.StrUtil;
     import org.dromara.hutool.core.text.CharUtil;
    @@ -53,8 +54,9 @@ public class ISO8601DateParser extends DefaultDateBasic implements DateParser {
     
     			final int patternLength = DatePattern.UTC_MS_PATTERN.length();
     			// 格式类似:2018-09-13T05:34:31.999Z,-4表示减去4个单引号的长度
    -			// -4 ~ -6范围表示匹配毫秒1~3位的情况
    -			if (length <= patternLength - 4 && length >= patternLength - 6) {
    +			// 2018-09-13T05:34:31.1Z - 2018-09-13T05:34:31.000000Z
    +			if (length <= patternLength && length >= patternLength - 6) {
    +				// 毫秒部分1-7位支持
     				return new DateTime(source, DatePattern.UTC_MS_FORMAT);
     			}
     		} else if (StrUtil.contains(source, '+')) {
    diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java
    index 64c97cefa..984c5f246 100644
    --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java
    +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java
    @@ -1088,4 +1088,11 @@ public class DateUtilTest {
     		Assertions.assertEquals(71, DateUtil.age(DateUtil.parse("1952-02-13"), DateUtil.parse("2023-02-14")));
     		Assertions.assertEquals(0, DateUtil.age(DateUtil.parse("2023-02-14"), DateUtil.parse("2023-02-14")));
     	}
    +
    +	@Test
    +	void issueI7H34NTest() {
    +		final DateTime parse = DateUtil.parse("2019-10-22T09:56:03.000123Z");
    +		Assertions.assertNotNull(parse);
    +		Assertions.assertEquals("2019-10-22 09:56:03", parse.toString());
    +	}
     }
    
    From 1804b7e6da6e55064f33b36e69dcf2a923b903fc Mon Sep 17 00:00:00 2001
    From: Looly 
    Date: Thu, 29 Jun 2023 17:28:52 +0800
    Subject: [PATCH 18/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8DPair=E5=BA=8F=E5=88=97?=
     =?UTF-8?q?=E5=8C=96=E8=BD=AC=E6=8D=A2=E6=97=A0=E6=95=88=E9=97=AE=E9=A2=98?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     .../core/convert/RegisterConverter.java       |   7 +-
     .../core/convert/impl/EntryConverter.java     |   4 +
     .../core/convert/impl/PairConverter.java      | 139 ++++++++++++++++++
     .../dromara/hutool/json/IssueI7GPGXTest.java  |  28 ++++
     4 files changed, 176 insertions(+), 2 deletions(-)
     create mode 100755 hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java
     create mode 100755 hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java
    
    diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java
    index 26bb55a97..0de77c32b 100644
    --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java
    +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java
    @@ -15,7 +15,9 @@ package org.dromara.hutool.core.convert;
     import org.dromara.hutool.core.convert.impl.*;
     import org.dromara.hutool.core.date.DateTime;
     import org.dromara.hutool.core.lang.Opt;
    +import org.dromara.hutool.core.lang.tuple.Pair;
     import org.dromara.hutool.core.map.SafeConcurrentHashMap;
    +import org.dromara.hutool.core.reflect.TypeUtil;
     
     import javax.xml.datatype.XMLGregorianCalendar;
     import java.io.Serializable;
    @@ -65,7 +67,7 @@ public class RegisterConverter implements Converter, Serializable {
     	/**
     	 * 默认类型转换器
     	 */
    -	private Map defaultConverterMap;
    +	private Map, Converter> defaultConverterMap;
     	/**
     	 * 用户自定义类型转换器
     	 */
    @@ -120,7 +122,7 @@ public class RegisterConverter implements Converter, Serializable {
     	 * @return 转换器
     	 */
     	public Converter getDefaultConverter(final Type type) {
    -		return (null == defaultConverterMap) ? null : defaultConverterMap.get(type);
    +		return (null == defaultConverterMap) ? null : defaultConverterMap.get(TypeUtil.getClass(type));
     	}
     
     	/**
    @@ -214,5 +216,6 @@ public class RegisterConverter implements Converter, Serializable {
     		defaultConverterMap.put(StackTraceElement.class, new StackTraceElementConverter());// since 4.5.2
     		defaultConverterMap.put(Optional.class, new OptionalConverter());// since 5.0.0
     		defaultConverterMap.put(Opt.class, new OptConverter());// since 5.7.16
    +		defaultConverterMap.put(Pair.class, PairConverter.INSTANCE);// since 5.7.16
     	}
     }
    diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java
    index a0799818f..dfb04359d 100644
    --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java
    +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java
    @@ -16,6 +16,7 @@ import org.dromara.hutool.core.bean.BeanUtil;
     import org.dromara.hutool.core.convert.CompositeConverter;
     import org.dromara.hutool.core.convert.ConvertException;
     import org.dromara.hutool.core.convert.Converter;
    +import org.dromara.hutool.core.lang.tuple.Pair;
     import org.dromara.hutool.core.map.MapUtil;
     import org.dromara.hutool.core.reflect.ConstructorUtil;
     import org.dromara.hutool.core.reflect.TypeReference;
    @@ -72,6 +73,9 @@ public class EntryConverter implements Converter {
     		if (value instanceof Map.Entry) {
     			final Map.Entry entry = (Map.Entry) value;
     			map = MapUtil.of(entry.getKey(), entry.getValue());
    +		}else if (value instanceof Pair) {
    +			final Pair entry = (Pair) value;
    +			map = MapUtil.of(entry.getLeft(), entry.getRight());
     		}else if (value instanceof Map) {
     			map = (Map) value;
     		} else if (value instanceof CharSequence) {
    diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java
    new file mode 100755
    index 000000000..13b061678
    --- /dev/null
    +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java
    @@ -0,0 +1,139 @@
    +/*
    + * Copyright (c) 2023 looly(loolly@aliyun.com)
    + * Hutool is licensed under Mulan PSL v2.
    + * You can use this software according to the terms and conditions of the Mulan PSL v2.
    + * You may obtain a copy of Mulan PSL v2 at:
    + *          http://license.coscl.org.cn/MulanPSL2
    + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
    + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
    + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
    + * See the Mulan PSL v2 for more details.
    + */
    +
    +package org.dromara.hutool.core.convert.impl;
    +
    +import org.dromara.hutool.core.bean.BeanUtil;
    +import org.dromara.hutool.core.convert.CompositeConverter;
    +import org.dromara.hutool.core.convert.ConvertException;
    +import org.dromara.hutool.core.convert.Converter;
    +import org.dromara.hutool.core.lang.tuple.Pair;
    +import org.dromara.hutool.core.map.MapUtil;
    +import org.dromara.hutool.core.reflect.TypeReference;
    +import org.dromara.hutool.core.reflect.TypeUtil;
    +import org.dromara.hutool.core.text.CharUtil;
    +import org.dromara.hutool.core.text.StrUtil;
    +
    +import java.lang.reflect.Type;
    +import java.util.Map;
    +
    +/**
    + * {@link Pair} 转换器,支持以下类型转为Pair
    + * 
      + *
    • {@link Map}
    • + *
    • {@link Map.Entry}
    • + *
    • 带分隔符的字符串,支持分隔符{@code :}、{@code =}、{@code ,}
    • + *
    • Bean,包含{@code getKey}和{@code getValue}方法
    • + *
    + * + * @author looly + */ +public class PairConverter implements Converter { + + /** + * 单例 + */ + public static final PairConverter INSTANCE = new PairConverter(); + + @Override + public Object convert(Type targetType, final Object value) throws ConvertException { + if (targetType instanceof TypeReference) { + targetType = ((TypeReference) targetType).getType(); + } + final Type keyType = TypeUtil.getTypeArgument(targetType, 0); + final Type valueType = TypeUtil.getTypeArgument(targetType, 1); + + return convert(keyType, valueType, value); + } + + /** + * 转换对象为指定键值类型的指定类型Map + * + * @param keyType 键类型 + * @param valueType 值类型 + * @param value 被转换的值 + * @return 转换后的Map + * @throws ConvertException 转换异常或不支持的类型 + */ + @SuppressWarnings("rawtypes") + public Pair convert(final Type keyType, final Type valueType, final Object value) + throws ConvertException { + Map map = null; + if (value instanceof Map.Entry) { + final Map.Entry entry = (Map.Entry) value; + map = MapUtil.of(entry.getKey(), entry.getValue()); + } else if (value instanceof Pair) { + final Pair entry = (Pair) value; + map = MapUtil.of(entry.getLeft(), entry.getRight()); + } else if (value instanceof Map) { + map = (Map) value; + } else if (value instanceof CharSequence) { + final CharSequence str = (CharSequence) value; + map = strToMap(str); + } else if (BeanUtil.isWritableBean(value.getClass())) { + map = BeanUtil.beanToMap(value); + } + + if (null != map) { + return mapToPair(keyType, valueType, map); + } + + throw new ConvertException("Unsupported to map from [{}] of type: {}", value, value.getClass().getName()); + } + + /** + * 字符串转单个键值对的Map,支持分隔符{@code :}、{@code =}、{@code ,} + * + * @param str 字符串 + * @return map or null + */ + private static Map strToMap(final CharSequence str) { + // key:value key=value key,value + final int index = StrUtil.indexOf(str, + c -> c == CharUtil.COLON || c == CharUtil.EQUAL || c == CharUtil.COMMA, + 0, str.length()); + + if (index > -1) { + return MapUtil.of(str.subSequence(0, index), str.subSequence(index + 1, str.length())); + } + return null; + } + + /** + * Map转Entry + * + * @param keyType 键类型 + * @param valueType 值类型 + * @param map 被转换的map + * @return Entry + */ + @SuppressWarnings("rawtypes") + private static Pair mapToPair(final Type keyType, final Type valueType, final Map map) { + + Object left = null; + Object right = null; + if (1 == map.size()) { + final Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); + left = entry.getKey(); + right = entry.getValue(); + } else if (2 == map.size()) { + left = map.get("left"); + right = map.get("right"); + } + + final CompositeConverter convert = CompositeConverter.getInstance(); + return Pair.of( + TypeUtil.isUnknown(keyType) ? left : convert.convert(keyType, left), + TypeUtil.isUnknown(valueType) ? right : convert.convert(valueType, right) + ); + } +} diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java new file mode 100755 index 000000000..a9497f01a --- /dev/null +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.json; + +import org.dromara.hutool.core.lang.tuple.Pair; +import org.dromara.hutool.core.reflect.TypeReference; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IssueI7GPGXTest { + @Test + public void pairToBeanTest() { + final Pair hutoolPair = new Pair<>("test1", true); + final String a = JSONUtil.toJsonStr(hutoolPair); + final Pair pair = JSONUtil.toBean(a, new TypeReference>() {}); + Assertions.assertEquals(hutoolPair, pair); + } +} From 887afc8984f13a6c2dff7e8d4229023786dc978b Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 29 Jun 2023 17:43:49 +0800 Subject: [PATCH 19/26] fix bug --- .../core/convert/RegisterConverter.java | 4 +- .../core/convert/impl/PairConverter.java | 22 ++-- .../core/convert/impl/TripleConverter.java | 102 ++++++++++++++++++ .../dromara/hutool/json/IssueI7GPGXTest.java | 11 +- 4 files changed, 126 insertions(+), 13 deletions(-) create mode 100755 hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java index 0de77c32b..d12378389 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java @@ -16,6 +16,7 @@ import org.dromara.hutool.core.convert.impl.*; import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.lang.tuple.Pair; +import org.dromara.hutool.core.lang.tuple.Triple; import org.dromara.hutool.core.map.SafeConcurrentHashMap; import org.dromara.hutool.core.reflect.TypeUtil; @@ -216,6 +217,7 @@ public class RegisterConverter implements Converter, Serializable { defaultConverterMap.put(StackTraceElement.class, new StackTraceElementConverter());// since 4.5.2 defaultConverterMap.put(Optional.class, new OptionalConverter());// since 5.0.0 defaultConverterMap.put(Opt.class, new OptConverter());// since 5.7.16 - defaultConverterMap.put(Pair.class, PairConverter.INSTANCE);// since 5.7.16 + defaultConverterMap.put(Pair.class, PairConverter.INSTANCE);// since 6.0.0 + defaultConverterMap.put(Triple.class, TripleConverter.INSTANCE);// since 6.0.0 } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java index 13b061678..6befa9814 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PairConverter.java @@ -32,7 +32,7 @@ import java.util.Map; *
  • {@link Map}
  • *
  • {@link Map.Entry}
  • *
  • 带分隔符的字符串,支持分隔符{@code :}、{@code =}、{@code ,}
  • - *
  • Bean,包含{@code getKey}和{@code getValue}方法
  • + *
  • Bean,包含{@code getLeft}和{@code getRight}方法
  • * * * @author looly @@ -49,23 +49,23 @@ public class PairConverter implements Converter { if (targetType instanceof TypeReference) { targetType = ((TypeReference) targetType).getType(); } - final Type keyType = TypeUtil.getTypeArgument(targetType, 0); - final Type valueType = TypeUtil.getTypeArgument(targetType, 1); + final Type leftType = TypeUtil.getTypeArgument(targetType, 0); + final Type rightType = TypeUtil.getTypeArgument(targetType, 1); - return convert(keyType, valueType, value); + return convert(leftType, rightType, value); } /** * 转换对象为指定键值类型的指定类型Map * - * @param keyType 键类型 - * @param valueType 值类型 + * @param leftType 键类型 + * @param rightType 值类型 * @param value 被转换的值 * @return 转换后的Map * @throws ConvertException 转换异常或不支持的类型 */ @SuppressWarnings("rawtypes") - public Pair convert(final Type keyType, final Type valueType, final Object value) + public Pair convert(final Type leftType, final Type rightType, final Object value) throws ConvertException { Map map = null; if (value instanceof Map.Entry) { @@ -79,12 +79,12 @@ public class PairConverter implements Converter { } else if (value instanceof CharSequence) { final CharSequence str = (CharSequence) value; map = strToMap(str); - } else if (BeanUtil.isWritableBean(value.getClass())) { + } else if (BeanUtil.isReadableBean(value.getClass())) { map = BeanUtil.beanToMap(value); } if (null != map) { - return mapToPair(keyType, valueType, map); + return mapToPair(leftType, rightType, map); } throw new ConvertException("Unsupported to map from [{}] of type: {}", value, value.getClass().getName()); @@ -109,12 +109,12 @@ public class PairConverter implements Converter { } /** - * Map转Entry + * Map转Pair * * @param keyType 键类型 * @param valueType 值类型 * @param map 被转换的map - * @return Entry + * @return Pair */ @SuppressWarnings("rawtypes") private static Pair mapToPair(final Type keyType, final Type valueType, final Map map) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java new file mode 100755 index 000000000..8d7f851e5 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TripleConverter.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.convert.impl; + +import org.dromara.hutool.core.bean.BeanUtil; +import org.dromara.hutool.core.convert.CompositeConverter; +import org.dromara.hutool.core.convert.ConvertException; +import org.dromara.hutool.core.convert.Converter; +import org.dromara.hutool.core.lang.tuple.Pair; +import org.dromara.hutool.core.lang.tuple.Triple; +import org.dromara.hutool.core.reflect.TypeReference; +import org.dromara.hutool.core.reflect.TypeUtil; + +import java.lang.reflect.Type; +import java.util.Map; + +/** + * {@link Triple} 转换器,支持以下类型转为Triple: + *
      + *
    • Bean,包含{@code getLeft}、{@code getMiddle}和{@code getRight}方法
    • + *
    + * + * @author looly + * @since 6.0.0 + */ +public class TripleConverter implements Converter { + + /** + * 单例 + */ + public static final TripleConverter INSTANCE = new TripleConverter(); + + @Override + public Object convert(Type targetType, final Object value) throws ConvertException { + if (targetType instanceof TypeReference) { + targetType = ((TypeReference) targetType).getType(); + } + final Type leftType = TypeUtil.getTypeArgument(targetType, 0); + final Type middileType = TypeUtil.getTypeArgument(targetType, 1); + final Type rightType = TypeUtil.getTypeArgument(targetType, 2); + + return convert(leftType, middileType, rightType, value); + } + + /** + * 转换对象为指定键值类型的指定类型Map + * + * @param leftType 键类型 + * @param middleType 中值类型 + * @param rightType 值类型 + * @param value 被转换的值 + * @return 转换后的Map + * @throws ConvertException 转换异常或不支持的类型 + */ + @SuppressWarnings("rawtypes") + public Triple convert(final Type leftType, final Type middleType, final Type rightType, final Object value) + throws ConvertException { + Map map = null; + if (BeanUtil.isReadableBean(value.getClass())) { + map = BeanUtil.beanToMap(value); + } + + if (null != map) { + return mapToTriple(leftType, middleType, rightType, map); + } + + throw new ConvertException("Unsupported to map from [{}] of type: {}", value, value.getClass().getName()); + } + + /** + * Map转Entry + * + * @param leftType 键类型 + * @param rightType 值类型 + * @param map 被转换的map + * @return Entry + */ + @SuppressWarnings("rawtypes") + private static Triple mapToTriple(final Type leftType, final Type middleType, final Type rightType, final Map map) { + + final Object left = map.get("left"); + final Object middle = map.get("middle"); + final Object right = map.get("right"); + + final CompositeConverter convert = CompositeConverter.getInstance(); + return Triple.of( + TypeUtil.isUnknown(leftType) ? left : convert.convert(leftType, left), + TypeUtil.isUnknown(middleType) ? middle : convert.convert(middleType, middle), + TypeUtil.isUnknown(rightType) ? right : convert.convert(rightType, right) + ); + } +} diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java index a9497f01a..7de290e71 100755 --- a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java @@ -13,6 +13,7 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.lang.tuple.Pair; +import org.dromara.hutool.core.lang.tuple.Triple; import org.dromara.hutool.core.reflect.TypeReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -20,9 +21,17 @@ import org.junit.jupiter.api.Test; public class IssueI7GPGXTest { @Test public void pairToBeanTest() { - final Pair hutoolPair = new Pair<>("test1", true); + final Pair hutoolPair = Pair.of("test1", true); final String a = JSONUtil.toJsonStr(hutoolPair); final Pair pair = JSONUtil.toBean(a, new TypeReference>() {}); Assertions.assertEquals(hutoolPair, pair); } + + @Test + void tripleToBeanTest() { + final Triple hutoolTriple = Triple.of("aaa", 123, true); + final String a = JSONUtil.toJsonStr(hutoolTriple); + final Triple pair = JSONUtil.toBean(a, new TypeReference>() {}); + Assertions.assertEquals(hutoolTriple, pair); + } } From 77bd2d4336a2f756b9c4503af12ecbcbee8a36c0 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 29 Jun 2023 18:00:20 +0800 Subject: [PATCH 20/26] add Converter --- .../core/convert/RegisterConverter.java | 2 + .../core/convert/impl/TupleConverter.java | 39 +++++++++++++++++++ .../hutool/core/lang/tuple/Triple.java | 4 +- .../dromara/hutool/core/lang/tuple/Tuple.java | 11 ++++++ .../dromara/hutool/json/IssueI7GPGXTest.java | 10 +++++ 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100755 hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TupleConverter.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java index d12378389..26a12b0bb 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java @@ -17,6 +17,7 @@ import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.lang.tuple.Pair; import org.dromara.hutool.core.lang.tuple.Triple; +import org.dromara.hutool.core.lang.tuple.Tuple; import org.dromara.hutool.core.map.SafeConcurrentHashMap; import org.dromara.hutool.core.reflect.TypeUtil; @@ -219,5 +220,6 @@ public class RegisterConverter implements Converter, Serializable { defaultConverterMap.put(Opt.class, new OptConverter());// since 5.7.16 defaultConverterMap.put(Pair.class, PairConverter.INSTANCE);// since 6.0.0 defaultConverterMap.put(Triple.class, TripleConverter.INSTANCE);// since 6.0.0 + defaultConverterMap.put(Tuple.class, TupleConverter.INSTANCE);// since 6.0.0 } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TupleConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TupleConverter.java new file mode 100755 index 000000000..b0ece7102 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TupleConverter.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.convert.impl; + +import org.dromara.hutool.core.convert.ConvertException; +import org.dromara.hutool.core.convert.Converter; +import org.dromara.hutool.core.lang.tuple.Tuple; + +import java.lang.reflect.Type; + +/** + * {@link Tuple}转换器 + * + * @author looly + * @since 6.0.0 + */ +public class TupleConverter implements Converter { + + /** + * 单例 + */ + public static final TupleConverter INSTANCE = new TupleConverter(); + + @Override + public Object convert(final Type targetType, final Object value) throws ConvertException { + final Object[] convert = (Object[]) ArrayConverter.INSTANCE.convert(Object[].class, value); + return Tuple.of(convert); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Triple.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Triple.java index 3f4717578..6d1982fdf 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Triple.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Triple.java @@ -26,8 +26,6 @@ import java.util.Objects; public class Triple extends Pair { private static final long serialVersionUID = 1L; - protected M middle; - /** * 构建Triple对象 * @@ -44,6 +42,8 @@ public class Triple extends Pair { return new Triple<>(left, middle, right); } + protected M middle; + /** * 构造 * diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Tuple.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Tuple.java index 47a8465a4..9f5469552 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Tuple.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/tuple/Tuple.java @@ -35,6 +35,17 @@ import java.util.stream.StreamSupport; public class Tuple implements Iterable, Serializable, Cloneable { private static final long serialVersionUID = -7689304393482182157L; + /** + * 构建Tuple对象 + * + * @param members 成员数组 + * @return Tuple + * @since 6.0.0 + */ + public static Tuple of(final Object... members) { + return new Tuple(members); + } + private final Object[] members; private int hashCode; private boolean cacheHash; diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java index 7de290e71..4e9343670 100755 --- a/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java +++ b/hutool-json/src/test/java/org/dromara/hutool/json/IssueI7GPGXTest.java @@ -12,8 +12,10 @@ package org.dromara.hutool.json; +import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.lang.tuple.Pair; import org.dromara.hutool.core.lang.tuple.Triple; +import org.dromara.hutool.core.lang.tuple.Tuple; import org.dromara.hutool.core.reflect.TypeReference; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -34,4 +36,12 @@ public class IssueI7GPGXTest { final Triple pair = JSONUtil.toBean(a, new TypeReference>() {}); Assertions.assertEquals(hutoolTriple, pair); } + + @Test + void tupleToBeanTest() { + final Tuple hutoolTriple = Tuple.of("aaa", 123, true); + final String a = JSONUtil.toJsonStr(hutoolTriple); + final Tuple pair = JSONUtil.toBean(a, Tuple.class); + Assertions.assertEquals(hutoolTriple, pair); + } } From c4373832b8a1f917977af79510418d86f0a3fc72 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 29 Jun 2023 18:03:51 +0800 Subject: [PATCH 21/26] fix test --- .../src/test/java/org/dromara/hutool/db/meta/MetaUtilTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hutool-db/src/test/java/org/dromara/hutool/db/meta/MetaUtilTest.java b/hutool-db/src/test/java/org/dromara/hutool/db/meta/MetaUtilTest.java index 85f944093..93161e448 100644 --- a/hutool-db/src/test/java/org/dromara/hutool/db/meta/MetaUtilTest.java +++ b/hutool-db/src/test/java/org/dromara/hutool/db/meta/MetaUtilTest.java @@ -22,7 +22,7 @@ public class MetaUtilTest { @Test public void getTablesTest() { final List tables = MetaUtil.getTables(ds); - Assertions.assertEquals("user", tables.get(0)); + Assertions.assertTrue(tables.contains("user")); } @Test From 566c43d464929ca6a8cae7de15d8f333a043844e Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 30 Jun 2023 10:13:15 +0800 Subject: [PATCH 22/26] =?UTF-8?q?RandomUtil=E5=A2=9E=E5=8A=A0=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E6=98=AF=E5=90=A6=E5=8C=85=E5=90=AB=E8=BE=B9=E7=95=8C?= =?UTF-8?q?=E7=9A=84=E9=87=8D=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/hutool/core/date/DateUtil.java | 4 + .../dromara/hutool/core/util/RandomUtil.java | 243 +++++++++++++----- 2 files changed, 179 insertions(+), 68 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java index b01796129..72f08400f 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java @@ -57,6 +57,7 @@ public class DateUtil extends CalendarUtil { "gmt", "ut", "utc", "est", "edt", "cst", "cdt", "mst", "mdt", "pst", "pdt"// 时间标准 }; + // region ----- date /** * 当前时间,转换为{@link DateTime}对象 * @@ -184,6 +185,7 @@ public class DateUtil extends CalendarUtil { } return new DateTime(temporalAccessor); } + // endregion /** * 当前时间的时间戳 @@ -1688,6 +1690,7 @@ public class DateUtil extends CalendarUtil { return sb.toString(); } + // region ----- range /** * 创建日期范围生成器 * @@ -1793,6 +1796,7 @@ public class DateUtil extends CalendarUtil { public static List rangeToList(final Date start, final Date end, final DateField unit, final int step) { return ListUtil.of((Iterable) new DateRange(start, end, unit, step)); } + // endregion /** * 通过生日计算星座 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java index f34278d87..9be5f6738 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java @@ -56,6 +56,8 @@ public class RandomUtil { */ public static final String BASE_CHAR_NUMBER = BASE_CHAR.toUpperCase() + BASE_CHAR_NUMBER_LOWER; + // region ----- get or create Random + /** * 获取随机数生成器对象
    * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 @@ -165,6 +167,7 @@ public class RandomUtil { public static Random getRandom(final boolean isSecure) { return isSecure ? getSecureRandom() : getRandom(); } + // endregion /** * 获得随机Boolean值 @@ -176,6 +179,18 @@ public class RandomUtil { return 0 == randomInt(2); } + /** + * 随机bytes + * + * @param length 长度 + * @return bytes + */ + public static byte[] randomBytes(final int length) { + final byte[] bytes = new byte[length]; + getRandom().nextBytes(bytes); + return bytes; + } + /** * 随机汉字('\u4E00'-'\u9FFF') * @@ -186,16 +201,7 @@ public class RandomUtil { return (char) randomInt('\u4E00', '\u9FFF'); } - /** - * 获得指定范围内的随机数 - * - * @param min 最小数(包含) - * @param max 最大数(不包含) - * @return 随机数 - */ - public static int randomInt(final int min, final int max) { - return getRandom().nextInt(min, max); - } + // region ----- randomInt /** * 获得随机数int值 @@ -210,27 +216,63 @@ public class RandomUtil { /** * 获得指定范围内的随机数 [0,limit) * - * @param limit 限制随机数的范围,不包括这个数 + * @param limitExclude 限制随机数的范围,不包括这个数 * @return 随机数 * @see Random#nextInt(int) */ - public static int randomInt(final int limit) { - return getRandom().nextInt(limit); + public static int randomInt(final int limitExclude) { + return getRandom().nextInt(limitExclude); } /** - * 获得指定范围内的随机数[min, max) + * 获得指定范围内的随机数 * - * @param min 最小数(包含) - * @param max 最大数(不包含) + * @param minInclude 最小数(包含) + * @param maxExclude 最大数(不包含) * @return 随机数 - * @see ThreadLocalRandom#nextLong(long, long) - * @since 3.3.0 */ - public static long randomLong(final long min, final long max) { - return getRandom().nextLong(min, max); + public static int randomInt(final int minInclude, final int maxExclude) { + return randomInt(minInclude, maxExclude, true, false); } + /** + * 获得指定范围内的随机数 + * + * @param min 最小数 + * @param max 最大数 + * @param includeMin 是否包含最小值 + * @param includeMax 是否包含最大值 + * @return 随机数 + */ + public static int randomInt(int min, int max, final boolean includeMin, final boolean includeMax) { + if (!includeMin) { + min++; + } + if (includeMax) { + max--; + } + return getRandom().nextInt(min, max); + } + + /** + * 创建指定长度的随机索引 + * + * @param length 长度 + * @return 随机索引 + * @since 5.2.1 + */ + public static int[] randomInts(final int length) { + final int[] range = NumberUtil.range(length); + for (int i = 0; i < length; i++) { + final int random = randomInt(i, length); + ArrayUtil.swap(range, i, random); + } + return range; + } + // endregion + + // region ----- randomLong + /** * 获得随机数 * @@ -245,39 +287,114 @@ public class RandomUtil { /** * 获得指定范围内的随机数 [0,limit) * - * @param limit 限制随机数的范围,不包括这个数 + * @param limitExclude 限制随机数的范围,不包括这个数 * @return 随机数 * @see ThreadLocalRandom#nextLong(long) */ - public static long randomLong(final long limit) { - return getRandom().nextLong(limit); + public static long randomLong(final long limitExclude) { + return getRandom().nextLong(limitExclude); + } + + /** + * 获得指定范围内的随机数[min, max) + * + * @param minInclude 最小数(包含) + * @param maxExclude 最大数(不包含) + * @return 随机数 + * @see ThreadLocalRandom#nextLong(long, long) + * @since 3.3.0 + */ + public static long randomLong(final long minInclude, final long maxExclude) { + return randomLong(minInclude, maxExclude, true, false); } /** * 获得指定范围内的随机数 * - * @param min 最小数(包含) - * @param max 最大数(不包含) + * @param min 最小数 + * @param max 最大数 + * @param includeMin 是否包含最小值 + * @param includeMax 是否包含最大值 + * @return 随机数 + */ + public static long randomLong(long min, long max, final boolean includeMin, final boolean includeMax) { + if (!includeMin) { + min++; + } + if (includeMax) { + max--; + } + return getRandom().nextLong(min, max); + } + // endregion + + // region ----- randomFloat + + /** + * 获得随机数[0, 1) + * + * @return 随机数 + * @see ThreadLocalRandom#nextFloat() + */ + public static float randomFloat() { + return getRandom().nextFloat(); + } + + /** + * 获得指定范围内的随机数 [0,limit) + * + * @param limitExclude 限制随机数的范围,不包括这个数 + * @return 随机数 + */ + public static float randomFloat(final float limitExclude) { + return randomFloat(0, limitExclude); + } + + /** + * 获得指定范围内的随机数[min, max) + * + * @param minInclude 最小数(包含) + * @param maxExclude 最大数(不包含) + * @return 随机数 + * @see ThreadLocalRandom#nextFloat() + */ + public static float randomFloat(final float minInclude, final float maxExclude) { + if (minInclude == maxExclude) { + return minInclude; + } + + return minInclude + ((maxExclude - minInclude) * getRandom().nextFloat()); + } + // endregion + + // region ----- randomDouble + + /** + * 获得指定范围内的随机数 + * + * @param minInclude 最小数(包含) + * @param maxExclude 最大数(不包含) * @return 随机数 * @see ThreadLocalRandom#nextDouble(double, double) * @since 3.3.0 */ - public static double randomDouble(final double min, final double max) { - return getRandom().nextDouble(min, max); + public static double randomDouble(final double minInclude, final double maxExclude) { + return getRandom().nextDouble(minInclude, maxExclude); } /** * 获得指定范围内的随机数 * - * @param min 最小数(包含) - * @param max 最大数(不包含) + * @param minInclude 最小数(包含) + * @param maxExclude 最大数(不包含) * @param scale 保留小数位数 * @param roundingMode 保留小数的模式 {@link RoundingMode} * @return 随机数 * @since 4.0.8 */ - public static double randomDouble(final double min, final double max, final int scale, final RoundingMode roundingMode) { - return NumberUtil.round(randomDouble(min, max), scale, roundingMode).doubleValue(); + public static double randomDouble(final double minInclude, final double maxExclude, final int scale, + final RoundingMode roundingMode) { + return NumberUtil.round(randomDouble(minInclude, maxExclude), scale, roundingMode).doubleValue(); } /** @@ -327,6 +444,9 @@ public class RandomUtil { public static double randomDouble(final double limit, final int scale, final RoundingMode roundingMode) { return NumberUtil.round(randomDouble(limit), scale, roundingMode).doubleValue(); } + // endregion + + // region ----- randomBigDecimal /** * 获得指定范围内的随机数[0, 1) @@ -341,37 +461,28 @@ public class RandomUtil { /** * 获得指定范围内的随机数 [0,limit) * - * @param limit 最大数(不包含) + * @param limitExclude 最大数(不包含) * @return 随机数 * @since 4.0.9 */ - public static BigDecimal randomBigDecimal(final BigDecimal limit) { - return NumberUtil.toBigDecimal(getRandom().nextDouble(limit.doubleValue())); + public static BigDecimal randomBigDecimal(final BigDecimal limitExclude) { + return NumberUtil.toBigDecimal(getRandom().nextDouble(limitExclude.doubleValue())); } /** * 获得指定范围内的随机数 * - * @param min 最小数(包含) - * @param max 最大数(不包含) + * @param minInclude 最小数(包含) + * @param maxExclude 最大数(不包含) * @return 随机数 * @since 4.0.9 */ - public static BigDecimal randomBigDecimal(final BigDecimal min, final BigDecimal max) { - return NumberUtil.toBigDecimal(getRandom().nextDouble(min.doubleValue(), max.doubleValue())); + public static BigDecimal randomBigDecimal(final BigDecimal minInclude, final BigDecimal maxExclude) { + return NumberUtil.toBigDecimal(getRandom().nextDouble(minInclude.doubleValue(), maxExclude.doubleValue())); } + // endregion - /** - * 随机bytes - * - * @param length 长度 - * @return bytes - */ - public static byte[] randomBytes(final int length) { - final byte[] bytes = new byte[length]; - getRandom().nextBytes(bytes); - return bytes; - } + // region ----- randomEle /** * 随机获得列表中的元素 @@ -470,8 +581,8 @@ public class RandomUtil { /** * 生成从种子中获取随机数字 * - * @param size 指定产生随机数的个数 - * @param seed 种子,用于取随机数的int池 + * @param size 指定产生随机数的个数 + * @param seed 种子,用于取随机数的int池 * @return 随机int数组 * @since 5.4.5 */ @@ -514,22 +625,9 @@ public class RandomUtil { return result; } + // endregion - /** - * 创建指定长度的随机索引 - * - * @param length 长度 - * @return 随机索引 - * @since 5.2.1 - */ - public static int[] randomInts(final int length) { - final int[] range = NumberUtil.range(length); - for (int i = 0; i < length; i++) { - final int random = randomInt(i, length); - ArrayUtil.swap(range, i, random); - } - return range; - } + // region ----- randomString /** * 获得一个随机的字符串(只包含数字和大小写字母) @@ -596,7 +694,7 @@ public class RandomUtil { if (StrUtil.isEmpty(baseString)) { return StrUtil.EMPTY; } - if(length < 1){ + if (length < 1) { length = 1; } @@ -608,6 +706,9 @@ public class RandomUtil { } return sb.toString(); } + // endregion + + // region ---- randomChar /** * 随机数字,数字为0~9单个数字 @@ -639,6 +740,9 @@ public class RandomUtil { public static char randomChar(final String baseString) { return baseString.charAt(randomInt(baseString.length())); } + // endregion + + // region ----- weightRandom /** * 带有权重的随机生成器 @@ -663,6 +767,9 @@ public class RandomUtil { public static WeightRandomSelector weightRandom(final Iterable> weightObjs) { return new WeightRandomSelector<>(weightObjs); } + // endregion + + // region ----- randomDate /** * 以当天为基准,随机产生一个日期 @@ -693,5 +800,5 @@ public class RandomUtil { return DateUtil.offset(baseDate, dateField, randomInt(min, max)); } - + // endregion } From 63c1c7ab93287866a4521fec35dfffd64abd9019 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 30 Jun 2023 10:29:05 +0800 Subject: [PATCH 23/26] fix code --- .../hutool/core/text/CharSequenceUtil.java | 12 +++++++++ .../core/text/CharSequenceUtilTest.java | 26 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java index ab5d317dd..8431b73d2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java @@ -3188,6 +3188,18 @@ public class CharSequenceUtil extends StrValidator { return sub(string, 0, length) + "..."; } + /** + * 截断字符串,使用UTF8编码为字节后不超过maxBytes长度 + * + * @param str 原始字符串 + * @param maxBytesLength 最大字节数 + * @param appendDots 截断后是否追加省略号(...) + * @return 限制后的长度 + */ + public static String limitByteLengthUtf8(final String str, final int maxBytesLength, final boolean appendDots) { + return limitByteLength(str, CharsetUtil.UTF_8, maxBytesLength, 4, appendDots); + } + /** * 截断字符串,使用其按照指定编码为字节后不超过maxBytes长度 * diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/text/CharSequenceUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/text/CharSequenceUtilTest.java index ee54a47ea..7588123e9 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/text/CharSequenceUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/text/CharSequenceUtilTest.java @@ -287,9 +287,33 @@ public class CharSequenceUtilTest { @Test void codeLengthTest() { - String a = "🍒🐽"; + final String a = "🍒🐽"; final int i = StrUtil.codeLength(a); Assertions.assertEquals(4, a.length()); Assertions.assertEquals(2, i); } + + @Test + public void limitByteLengthUtf8Test() { + final String str = "这是This一段中英文"; + String ret = StrUtil.limitByteLengthUtf8(str, 12, true); + Assertions.assertEquals("这是Thi...", ret); + + ret = StrUtil.limitByteLengthUtf8(str, 13, true); + Assertions.assertEquals("这是This...", ret); + + ret = StrUtil.limitByteLengthUtf8(str, 14, true); + Assertions.assertEquals("这是This...", ret); + + ret = StrUtil.limitByteLengthUtf8(str, 999, true); + Assertions.assertEquals(str, ret); + } + + @Test + public void limitByteLengthTest() { + final String str = "This is English"; + final String ret = StrUtil.limitByteLength(str, CharsetUtil.ISO_8859_1,10, 1, false); + Assertions.assertEquals("This is En", ret); + + } } From 436ac6557c03e4f70ed7422467731e8978c84273 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 30 Jun 2023 10:36:31 +0800 Subject: [PATCH 24/26] fix code --- .../org/dromara/hutool/core/text/CharSequenceUtil.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java index 8431b73d2..55ef5c4f8 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java @@ -3196,7 +3196,7 @@ public class CharSequenceUtil extends StrValidator { * @param appendDots 截断后是否追加省略号(...) * @return 限制后的长度 */ - public static String limitByteLengthUtf8(final String str, final int maxBytesLength, final boolean appendDots) { + public static String limitByteLengthUtf8(final CharSequence str, final int maxBytesLength, final boolean appendDots) { return limitByteLength(str, CharsetUtil.UTF_8, maxBytesLength, 4, appendDots); } @@ -3210,15 +3210,15 @@ public class CharSequenceUtil extends StrValidator { * @param appendDots 截断后是否追加省略号(...) * @return 限制后的长度 */ - public static String limitByteLength(final String str, final Charset charset, final int maxBytesLength, + public static String limitByteLength(final CharSequence str, final Charset charset, final int maxBytesLength, final int factor, final boolean appendDots) { //字符数*速算因子<=最大字节数 if (str == null || str.length() * factor <= maxBytesLength) { - return str; + return str(str); } - final byte[] sba = str.getBytes(charset); + final byte[] sba = ByteUtil.toBytes(str, charset); if (sba.length <= maxBytesLength) { - return str; + return str(str); } //限制字节数 final int limitBytes; From b6ee14c8e2dcf0a392a4933b4323082d856030de Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 30 Jun 2023 11:03:52 +0800 Subject: [PATCH 25/26] fix code --- .../dromara/hutool/core/math/MathUtil.java | 72 +++++++++++++++---- 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java index bbcbe4c5c..15eaf731b 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java @@ -18,6 +18,8 @@ import org.dromara.hutool.core.text.StrUtil; import java.math.BigInteger; import java.util.List; +import static java.lang.Math.min; + /** * 数学相关方法工具类
    * 此工具类与{@link NumberUtil}属于一类工具,NumberUtil偏向于简单数学计算的封装,MathUtil偏向复杂数学计算 @@ -31,11 +33,12 @@ public class MathUtil { * 0-20对应的阶乘,超过20的阶乘会超过Long.MAX_VALUE */ private static final long[] FACTORIALS = new long[]{ - 1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L, - 87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L, - 2432902008176640000L}; + 1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L, + 87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L, + 2432902008176640000L}; //--------------------------------------------------------------------------------------------- Arrangement + /** * 计算排列数,即A(n, m) = n!/(n-m)! * @@ -61,7 +64,7 @@ public class MathUtil { * 排列选择(从列表中选择n个排列) * * @param datas 待选列表 - * @param m 选择个数 + * @param m 选择个数 * @return 所有排列列表 */ public static List arrangementSelect(final String[] datas, final int m) { @@ -79,6 +82,7 @@ public class MathUtil { } //--------------------------------------------------------------------------------------------- Combination + /** * 计算组合数,即C(n, m) = n!/((n-m)!* m!) * @@ -94,7 +98,7 @@ public class MathUtil { * 组合选择(从列表中选择n个组合) * * @param datas 待选列表 - * @param m 选择个数 + * @param m 选择个数 * @return 所有组合列表 */ public static List combinationSelect(final String[] datas, final int m) { @@ -270,19 +274,57 @@ public class MathUtil { } /** - * 最大公约数 + * 最大公约数
    + * 见:https://stackoverflow.com/questions/4009198/java-get-greatest-common-divisor
    + * 来自Guava的IntMath.gcd * - * @param m 第一个值 - * @param n 第二个值 + * @param a 第一个值 + * @param b 第二个值 * @return 最大公约数 */ - public static int divisor(int m, int n) { - while (m % n != 0) { - final int temp = m % n; - m = n; - n = temp; + public static int gcd(int a, int b) { + /* + * The reason we require both arguments to be >= 0 is because otherwise, what do you return on + * gcd(0, Integer.MIN_VALUE)? BigInteger.gcd would return positive 2^31, but positive 2^31 + * isn't an int. + */ + Assert.isTrue(a >= 0, "a must be >= 0"); + Assert.isTrue(b >= 0, "b must be >= 0"); + if (a == 0) { + // 0 % b == 0, so b divides a, but the converse doesn't hold. + // BigInteger.gcd is consistent with this decision. + return b; + } else if (b == 0) { + return a; // similar logic } - return n; + /* + * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm. + * This is >40% faster than the Euclidean algorithm in benchmarks. + */ + final int aTwos = Integer.numberOfTrailingZeros(a); + a >>= aTwos; // divide out all 2s + final int bTwos = Integer.numberOfTrailingZeros(b); + b >>= bTwos; // divide out all 2s + while (a != b) { // both a, b are odd + // The key to the binary GCD algorithm is as follows: + // Both a and b are odd. Assume a > b; then gcd(a - b, b) = gcd(a, b). + // But in gcd(a - b, b), a - b is even and b is odd, so we can divide out powers of two. + + // We bend over backwards to avoid branching, adapting a technique from + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax + + final int delta = a - b; // can't overflow, since a and b are nonnegative + + final int minDeltaOrZero = delta & (delta >> (Integer.SIZE - 1)); + // equivalent to Math.min(delta, 0) + + a = delta - minDeltaOrZero - minDeltaOrZero; // sets a to Math.abs(a - b) + // a is now nonnegative and even + + b += minDeltaOrZero; // sets b to min(old a, b) + a >>= Integer.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b + } + return a << min(aTwos, bTwos); } /** @@ -293,7 +335,7 @@ public class MathUtil { * @return 最小公倍数 */ public static int multiple(final int m, final int n) { - return m * n / divisor(m, n); + return m * n / gcd(m, n); } private static int mathSubNode(final int selectNum, final int minNum) { From e9aa7b327927491770c1d4610350c7cd6a1ae9d9 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 30 Jun 2023 18:17:34 +0800 Subject: [PATCH 26/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8DTypeUtil.getTypeArgumen?= =?UTF-8?q?t=E5=AF=B9=E5=AE=9E=E7=8E=B0=E6=8E=A5=E5=8F=A3=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=B8=8D=E5=85=A8=E9=9D=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/hutool/core/reflect/TypeUtil.java | 74 +++++++++++++++---- .../hutool/core/reflect/IssueI7CRIWTest.java | 60 +++++++++++++++ 2 files changed, 121 insertions(+), 13 deletions(-) create mode 100755 hutool-core/src/test/java/org/dromara/hutool/core/reflect/IssueI7CRIWTest.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java index 86ec1b7d5..768065df8 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/TypeUtil.java @@ -13,6 +13,7 @@ package org.dromara.hutool.core.reflect; import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.util.ObjUtil; import java.lang.reflect.Field; @@ -21,6 +22,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; +import java.util.List; import java.util.Map; /** @@ -276,23 +278,69 @@ public class TypeUtil { * @since 4.5.2 */ public static ParameterizedType toParameterizedType(final Type type) { - ParameterizedType result = null; + return toParameterizedType(type, 0); + } + + /** + * 将{@link Type} 转换为{@link ParameterizedType}
    + * {@link ParameterizedType}用于获取当前类或父类中泛型参数化后的类型
    + * 一般用于获取泛型参数具体的参数类型,例如: + * + *
    {@code
    +	 *   class A
    +	 *   class B extends A;
    +	 * }
    + *

    + * 通过此方法,传入B.class即可得到B对应的{@link ParameterizedType},从而获取到String + * + * @param type {@link Type} + * @param interfaceIndex 实现的第几个接口 + * @return {@link ParameterizedType} + * @since 4.5.2 + */ + public static ParameterizedType toParameterizedType(final Type type, final int interfaceIndex) { if (type instanceof ParameterizedType) { - result = (ParameterizedType) type; - } else if (type instanceof Class) { - final Class clazz = (Class) type; - Type genericSuper = clazz.getGenericSuperclass(); - if (null == genericSuper || Object.class.equals(genericSuper)) { - // 如果类没有父类,而是实现一些定义好的泛型接口,则取接口的Type - final Type[] genericInterfaces = clazz.getGenericInterfaces(); - if (ArrayUtil.isNotEmpty(genericInterfaces)) { - // 默认取第一个实现接口的泛型Type - genericSuper = genericInterfaces[0]; + return (ParameterizedType) type; + } + + if (type instanceof Class) { + final ParameterizedType[] generics = getGenerics((Class) type); + if(generics.length > interfaceIndex){ + return generics[interfaceIndex]; + } + } + + return null; + } + + /** + * 获取指定类所有泛型父类和泛型接口 + * + * @param clazz 类 + * @return 泛型父类或接口数组 + * @since 6.0.0 + */ + public static ParameterizedType[] getGenerics(final Class clazz) { + final List result = ListUtil.of(false); + // 泛型父类(父类及祖类优先级高) + final Type genericSuper = clazz.getGenericSuperclass(); + if(null != genericSuper && !Object.class.equals(genericSuper)){ + final ParameterizedType parameterizedType = toParameterizedType(genericSuper); + if(null != parameterizedType){ + result.add(parameterizedType); + } + } + + // 泛型接口 + final Type[] genericInterfaces = clazz.getGenericInterfaces(); + if (ArrayUtil.isNotEmpty(genericInterfaces)) { + for (final Type genericInterface : genericInterfaces) { + if (genericInterface instanceof ParameterizedType) { + result.add((ParameterizedType) genericInterface); } } - result = toParameterizedType(genericSuper); } - return result; + return result.toArray(new ParameterizedType[0]); } /** diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/reflect/IssueI7CRIWTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/reflect/IssueI7CRIWTest.java new file mode 100755 index 000000000..313b710da --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/reflect/IssueI7CRIWTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.reflect; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Type; + +public class IssueI7CRIWTest { + + @Test + void getTypeArgumentsTest() { + // 无法从继承获取泛型,则从接口获取 + Type type = TypeUtil.getTypeArgument(C.class); + Assertions.assertEquals(type, String.class); + + // 继承和第一个接口都非泛型接口,则从找到的第一个泛型接口获取 + type = TypeUtil.getTypeArgument(D.class); + Assertions.assertEquals(type, String.class); + } + + static class A{ + + } + + static class AT{ + + } + + interface Face1{ + + } + + interface Face2{ + + } + + static class B extends A{ + + } + + static class C extends A implements Face1{ + + } + + static class D extends A implements Face2, Face1{ + + } +}