From 91178e45a876913329bb8af4e6dcecefec4cd9eb Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 29 Dec 2023 20:21:40 +0800 Subject: [PATCH] fix code --- .../dromara/hutool/core/io/file/PathUtil.java | 47 ++++++- .../hutool/core/io/watch/WatchMonitor.java | 12 +- .../core/io/watch/WatchServiceWrapper.java | 59 +++++++-- .../hutool/core/io/watch/WatchUtil.java | 115 ++++++++++-------- .../dromara/hutool/core/io/watch/Watcher.java | 8 +- .../core/io/watch/watchers/DelayWatcher.java | 30 +++-- .../core/io/watch/watchers/IgnoreWatcher.java | 44 ------- .../core/io/watch/watchers/SimpleWatcher.java | 27 +++- .../dromara/hutool/core/regex/RegexPool.java | 2 +- .../dromara/hutool/core/text/CharArray.java | 105 ++++++++++++++++ .../hutool/core/io/WatchMonitorTest.java | 70 ----------- .../hutool/core/io/file/PathUtilTest.java | 20 +++ .../core/io/watch/TestConsoleWatcher.java | 35 ++++++ .../core/io/watch/WatchMonitorTest.java | 44 +++++++ .../io/watch/WatchServiceWrapperTest.java | 22 ++++ 15 files changed, 442 insertions(+), 198 deletions(-) delete mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/IgnoreWatcher.java create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/text/CharArray.java delete mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/io/watch/TestConsoleWatcher.java create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchMonitorTest.java create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchServiceWrapperTest.java 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 dcd5a15ad..086156dd8 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 @@ -12,6 +12,7 @@ package org.dromara.hutool.core.io.file; +import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.util.CharsetUtil; @@ -32,6 +33,42 @@ import java.util.Set; * @since 5.4.1 */ public class PathUtil { + /** + * 拼接多个路径 + * + * @param firstPath 第一个路径 + * @param paths 其它路径 + * @return 拼接后的路径 + * @see Paths#get(String, String...) + */ + public static Path of(final String firstPath, final String... paths) { + return Paths.get(firstPath, paths); + } + + /** + * 拼接多个路径, + * + * @param firstPath 第一个路径 + * @param paths 其它路径 + * @return 拼接后的路径 + */ + public static Path of(Path firstPath, final Path... paths) { + if (ArrayUtil.isEmpty(paths)) { + return firstPath; + } + + for (final Path path : paths) { + if (null == path) { + continue; + } + if (null == firstPath) { + firstPath = path; + } else { + firstPath = firstPath.resolve(path); + } + } + return firstPath; + } /** * 目录是否为空 @@ -115,6 +152,7 @@ public class PathUtil { } // region ----- walkFiles + /** * 遍历指定path下的文件并做处理 * @@ -580,7 +618,9 @@ public class PathUtil { } /** - * 将Path路径转换为标准的绝对路径 + * 将Path路径转换为标准的绝对路径
+ * 如果{@link Path#isAbsolute()}为{@code true},表示已经是绝对路径,返回本身
+ * 如果是相对路径,则返回相对于系统默认目录的路径(一般为项目路径) * * @param path 文件或目录Path * @return 转换后的Path @@ -594,7 +634,10 @@ public class PathUtil { } /** - * 获取实际路径,路径文件必须存在 + * 获取实际路径,路径文件必须存在
+ * 如果给定Path是软链接(符号链接),则返回指向的实际链接
+ * 如果路径不存在,会直接抛出NoSuchFileException异常
+ * 无论给定是何种类型的路径,返回都是唯一的路径(总是equals) * * @param path 路径 * @return 实际路径 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java index b3f17c99a..1609f6ec3 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchMonitor.java @@ -22,7 +22,6 @@ import java.io.Closeable; import java.io.Serializable; import java.nio.file.Path; import java.nio.file.WatchEvent; -import java.nio.file.WatchService; /** * 路径监听器 @@ -61,7 +60,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable { * 构造 * * @param dir 字符串路径 - * @param events 监听事件列表 + * @param events 监听事件列表,如创建、修改和删除等 */ public WatchMonitor(final Path dir, final WatchEvent.Kind... events) { this(dir, 0, events); @@ -78,10 +77,10 @@ public class WatchMonitor extends Thread implements Closeable, Serializable { * * @param dir 路径 * @param maxDepth 递归目录的最大深度,当小于2时不递归下层目录 - * @param events 监听事件列表 + * @param events 监听事件列表,如创建、修改和删除等 */ public WatchMonitor(final Path dir, final int maxDepth, final WatchEvent.Kind... events) { - this.watchService = new WatchServiceWrapper().setEvents(events); + this.watchService = WatchServiceWrapper.of(events); this.dir = dir; this.maxDepth = maxDepth; this.init(); @@ -161,7 +160,6 @@ public class WatchMonitor extends Thread implements Closeable, Serializable { * 初始化包括: *
 	 * 1、解析传入的路径,判断其为目录还是文件
-	 * 2、创建{@link WatchService} 对象
 	 * 
* * @throws WatchException 监听异常,IO异常时抛出此异常 @@ -195,7 +193,9 @@ public class WatchMonitor extends Thread implements Closeable, Serializable { * @param watcher {@link Watcher} */ private void doTakeAndWatch(final Watcher watcher) { - this.watchService.watch(watcher, watchEvent -> null == file || file.endsWith(watchEvent.context().toString())); + this.watchService.watch(watcher, + // 对于文件监听,忽略目录下其他文件和目录的事件 + watchEvent -> null == file || file.endsWith(watchEvent.context().toString())); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServiceWrapper.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServiceWrapper.java index 2dce5c96e..ecea58e57 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServiceWrapper.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchServiceWrapper.java @@ -25,8 +25,11 @@ import java.util.function.BiConsumer; import java.util.function.Predicate; /** - * {@link WatchEvent} 包装类,提供可选的监听事件和监听选项
- * 通过多次调用{@link #registerPath(Path, int)} 可以递归注册多个路径 + * {@link WatchEvent} 包装类,提供可选的监听事件和监听选项,实现方法包括: + * * * @author looly * @since 6.0.0 @@ -34,12 +37,22 @@ import java.util.function.Predicate; public class WatchServiceWrapper implements WatchService, Wrapper, Serializable { private static final long serialVersionUID = 1L; + /** + * 创建WatchServiceWrapper + * + * @param events 监听事件列表,如新建、修改、删除等 + * @return WatchServiceWrapper + */ + public static WatchServiceWrapper of(final WatchEvent.Kind... events) { + return new WatchServiceWrapper(events); + } + /** * 监听服务 */ private final WatchService watchService; /** - * 监听事件列表 + * 监听事件列表,如新建、修改、删除等 */ private WatchEvent.Kind[] events; /** @@ -53,8 +66,10 @@ public class WatchServiceWrapper implements WatchService, Wrapper, /** * 构造 + * + * @param events 监听事件列表 */ - public WatchServiceWrapper() { + public WatchServiceWrapper(final WatchEvent.Kind... events) { //初始化监听 try { watchService = FileSystems.getDefault().newWatchService(); @@ -62,7 +77,7 @@ public class WatchServiceWrapper implements WatchService, Wrapper, throw new WatchException(e); } - isClosed = false; + this.events = events; } @Override @@ -148,6 +163,8 @@ public class WatchServiceWrapper implements WatchService, Wrapper, * * @param watchable 可注册对象,如Path * @return {@link WatchKey},如果为{@code null},表示注册失败 + * @see Watchable#register(WatchService, WatchEvent.Kind[]) + * @see Watchable#register(WatchService, WatchEvent.Kind[], WatchEvent.Modifier...) */ public WatchKey register(final Watchable watchable) { final WatchEvent.Kind[] kinds = ArrayUtil.defaultIfEmpty(this.events, WatchKind.ALL); @@ -174,17 +191,18 @@ public class WatchServiceWrapper implements WatchService, Wrapper, * * @param path 路径 * @param maxDepth 递归下层目录的最大深度 + * @return this */ - public void registerPath(final Path path, final int maxDepth) { + public WatchServiceWrapper registerPath(final Path path, final int maxDepth) { // 注册当前目录或文件 - final WatchKey watchKey = register(path); - if (null == watchKey) { + if (null == register(path)) { // 注册失败,跳过(可能目录或文件无权限) - return; + return this; } // 递归注册下一层层级的目录和文件 PathUtil.walkFiles(path, maxDepth, new SimpleFileVisitor() { + @SuppressWarnings("resource") @Override public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { //继续添加目录 @@ -192,10 +210,14 @@ public class WatchServiceWrapper implements WatchService, Wrapper, return super.postVisitDirectory(dir, exc); } }); + return this; } /** - * 执行事件获取并处理 + * 执行事件获取并处理
+ * {@link WatchEvent#context()}是实际操作的文件或目录的相对监听路径的Path,非绝对路径
+ * {@link WatchKey#watchable()}是监听的Path
+ * 此方法调用后阻塞线程,直到触发监听事件,执行后退出,无循环执行操作 * * @param watcher {@link Watcher} * @param watchFilter 监听过滤接口,通过实现此接口过滤掉不需要监听的情况,{@link Predicate#test(Object)}为{@code true}保留,null表示不过滤 @@ -217,7 +239,22 @@ public class WatchServiceWrapper implements WatchService, Wrapper, } /** - * 执行事件获取并处理 + * 执行事件获取并处理
+ * {@link WatchEvent#context()}是实际操作的文件或目录的相对监听路径的Path,非绝对路径
+ * {@link WatchKey#watchable()}是监听的Path
+ * 此方法调用后阻塞线程,直到触发监听事件,执行后退出,无循环执行操作 + * + * @param action 监听回调函数,实现此函数接口用于处理WatchEvent事件 + */ + public void watch(final BiConsumer, WatchKey> action) { + watch(action, null); + } + + /** + * 执行事件获取并处理
+ * {@link WatchEvent#context()}是实际操作的文件或目录的相对监听路径的Path,非绝对路径
+ * {@link WatchKey#watchable()}是监听的Path
+ * 此方法调用后阻塞线程,直到触发监听事件,执行后退出,无循环执行操作 * * @param action 监听回调函数,实现此函数接口用于处理WatchEvent事件 * @param watchFilter 监听过滤接口,通过实现此接口过滤掉不需要监听的情况,{@link Predicate#test(Object)}为{@code true}保留,null表示不过滤 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java index 24563e3e3..54eb5e9be 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/WatchUtil.java @@ -13,6 +13,8 @@ package org.dromara.hutool.core.io.watch; import org.dromara.hutool.core.io.IORuntimeException; +import org.dromara.hutool.core.io.file.PathUtil; +import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.net.url.UrlUtil; import java.io.File; @@ -36,10 +38,11 @@ import java.nio.file.Watchable; public class WatchUtil { // region ----- of + /** * 创建并初始化监听 * - * @param url URL + * @param url URL * @param events 监听的事件列表 * @return 监听对象 */ @@ -50,8 +53,8 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param url URL - * @param events 监听的事件列表 + * @param url URL + * @param events 监听的事件列表 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 * @return 监听对象 */ @@ -62,7 +65,7 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param uri URI + * @param uri URI * @param events 监听的事件列表 * @return 监听对象 */ @@ -73,8 +76,8 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param uri URI - * @param events 监听的事件列表 + * @param uri URI + * @param events 监听的事件列表 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 * @return 监听对象 */ @@ -85,7 +88,7 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param file 文件 + * @param file 文件 * @param events 监听的事件列表 * @return 监听对象 */ @@ -96,8 +99,8 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param file 文件 - * @param events 监听的事件列表 + * @param file 文件 + * @param events 监听的事件列表 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 * @return 监听对象 */ @@ -108,7 +111,7 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param path 路径 + * @param path 路径 * @param events 监听的事件列表 * @return 监听对象 */ @@ -119,8 +122,8 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param path 路径 - * @param events 监听的事件列表 + * @param path 路径 + * @param events 监听的事件列表 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 * @return 监听对象 */ @@ -131,7 +134,7 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param path 路径 + * @param path 路径 * @param events 监听事件列表 * @return 监听对象 */ @@ -142,8 +145,8 @@ public class WatchUtil { /** * 创建并初始化监听 * - * @param path 路径 - * @param events 监听事件列表 + * @param path 路径 + * @param events 监听事件列表 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 * @return 监听对象 */ @@ -153,10 +156,11 @@ public class WatchUtil { // endregion // region ----- ofAll + /** * 创建并初始化监听,监听所有事件 * - * @param url URL + * @param url URL * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ @@ -167,9 +171,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param url URL + * @param url URL * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ public static WatchMonitor ofAll(final URL url, final int maxDepth, final Watcher watcher) { @@ -179,7 +183,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param uri URI + * @param uri URI * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ @@ -190,9 +194,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param uri URI + * @param uri URI * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ public static WatchMonitor ofAll(final URI uri, final int maxDepth, final Watcher watcher) { @@ -202,7 +206,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param file 被监听文件 + * @param file 被监听文件 * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ @@ -213,9 +217,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param file 被监听文件 + * @param file 被监听文件 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ public static WatchMonitor ofAll(final File file, final int maxDepth, final Watcher watcher) { @@ -225,7 +229,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param path 路径 + * @param path 路径 * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ @@ -236,9 +240,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param path 路径 + * @param path 路径 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ public static WatchMonitor ofAll(final String path, final int maxDepth, final Watcher watcher) { @@ -248,7 +252,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param path 路径 + * @param path 路径 * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ @@ -259,9 +263,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听所有事件 * - * @param path 路径 + * @param path 路径 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} */ @SuppressWarnings("resource") @@ -271,10 +275,11 @@ public class WatchUtil { // endregion // region ----- ofModify + /** * 创建并初始化监听,监听修改事件 * - * @param url URL + * @param url URL * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 @@ -286,9 +291,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param url URL + * @param url URL * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 */ @@ -299,7 +304,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param uri URI + * @param uri URI * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 @@ -311,9 +316,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param uri URI + * @param uri URI * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 */ @@ -324,7 +329,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param file 被监听文件 + * @param file 被监听文件 * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 @@ -336,9 +341,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param file 被监听文件 + * @param file 被监听文件 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 */ @@ -349,7 +354,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param path 路径 + * @param path 路径 * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 @@ -361,9 +366,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param path 路径 + * @param path 路径 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 */ @@ -374,7 +379,7 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param path 路径 + * @param path 路径 * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 @@ -386,9 +391,9 @@ public class WatchUtil { /** * 创建并初始化监听,监听修改事件 * - * @param path 路径 + * @param path 路径 * @param maxDepth 当监听目录时,监听目录的最大深度,当设置值为1(或小于1)时,表示不递归监听子目录 - * @param watcher {@link Watcher} + * @param watcher {@link Watcher} * @return {@link WatchMonitor} * @since 4.5.2 */ @@ -403,16 +408,30 @@ public class WatchUtil { * 注册Watchable对象到WatchService服务 * * @param watchable 可注册对象 - * @param watcher WatchService对象 - * @param events 监听事件 + * @param watcher WatchService对象 + * @param events 监听事件 * @return {@link WatchKey} * @since 4.6.9 */ - public static WatchKey register(final Watchable watchable, final WatchService watcher, final WatchEvent.Kind... events){ + public static WatchKey register(final Watchable watchable, final WatchService watcher, final WatchEvent.Kind... events) { try { return watchable.register(watcher, events); } catch (final IOException e) { throw new IORuntimeException(e); } } + + /** + * 获取触发事件中相对监听Path的完整路径 + * + * @param event 事件 + * @param key {@link WatchKey} + * @return 完整路径 + */ + public static Path resolvePath(final WatchEvent event, final WatchKey key) { + Assert.notNull(event, "WatchEvent must be not null!"); + Assert.notNull(event, "WatchKey must be not null!"); + + return PathUtil.of((Path) key.watchable(), (Path) event.context()); + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java index 61ffd3c7e..d980e3c35 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/Watcher.java @@ -24,7 +24,7 @@ public interface Watcher { /** * 文件创建时执行的方法 * - * @param event 事件 + * @param event 事件,可通过{@link WatchEvent#context()}获取创建的文件或目录名称 * @param key 事件发生的{@link WatchKey},可以通过{@link WatchKey#watchable()}获取监听的Path路径 */ void onCreate(WatchEvent event, WatchKey key); @@ -33,7 +33,7 @@ public interface Watcher { * 文件修改时执行的方法
* 文件修改可能触发多次 * - * @param event 事件 + * @param event 事件,可通过{@link WatchEvent#context()}获取创建的文件或目录名称 * @param key 事件发生的{@link WatchKey},可以通过{@link WatchKey#watchable()}获取监听的Path路径 */ void onModify(WatchEvent event, WatchKey key); @@ -41,7 +41,7 @@ public interface Watcher { /** * 文件删除时执行的方法 * - * @param event 事件 + * @param event 事件,可通过{@link WatchEvent#context()}获取创建的文件或目录名称 * @param key 事件发生的{@link WatchKey},可以通过{@link WatchKey#watchable()}获取监听的Path路径 */ void onDelete(WatchEvent event, WatchKey key); @@ -49,7 +49,7 @@ public interface Watcher { /** * 事件丢失或出错时执行的方法 * - * @param event 事件 + * @param event 事件,可通过{@link WatchEvent#context()}获取创建的文件或目录名称 * @param key 事件发生的{@link WatchKey},可以通过{@link WatchKey#watchable()}获取监听的Path路径 */ void onOverflow(WatchEvent event, WatchKey key); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/DelayWatcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/DelayWatcher.java index 0d4f70f52..3e1e86119 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/DelayWatcher.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/DelayWatcher.java @@ -32,22 +32,30 @@ import java.util.Set; */ public class DelayWatcher implements Watcher { - /** Path集合。此集合用于去重在指定delay内多次触发的文件Path */ + /** + * Path集合。此集合用于去重在指定delay内多次触发的文件Path + */ private final Set eventSet = new ConcurrentHashSet<>(); - /** 实际处理 */ + /** + * 实际处理 + */ private final Watcher watcher; - /** 延迟,单位毫秒 */ + /** + * 延迟,单位毫秒 + */ private final long delay; //---------------------------------------------------------------------------------------------------------- Constructor start + /** * 构造 + * * @param watcher 实际处理触发事件的监视器{@link Watcher},不可以是{@code DelayWatcher} - * @param delay 延迟时间,单位毫秒 + * @param delay 延迟时间,单位毫秒 */ public DelayWatcher(final Watcher watcher, final long delay) { Assert.notNull(watcher); - if(watcher instanceof DelayWatcher) { + if (watcher instanceof DelayWatcher) { throw new IllegalArgumentException("Watcher must not be a DelayWatcher"); } this.watcher = watcher; @@ -57,9 +65,9 @@ public class DelayWatcher implements Watcher { @Override public void onModify(final WatchEvent event, final WatchKey key) { - if(this.delay < 1) { + if (this.delay < 1) { this.watcher.onModify(event, key); - }else { + } else { onDelayModify(event, key); } } @@ -80,14 +88,16 @@ public class DelayWatcher implements Watcher { } //---------------------------------------------------------------------------------------------------------- Private method start + /** * 触发延迟修改 + * * @param event 事件 - * @param key {@link WatchKey} + * @param key {@link WatchKey} */ private void onDelayModify(final WatchEvent event, final WatchKey key) { final Path eventPath = Paths.get(key.watchable().toString(), event.context().toString()); - if(eventSet.contains(eventPath)) { + if (eventSet.contains(eventPath)) { //此事件已经被触发过,后续事件忽略,等待统一处理。 return; } @@ -101,7 +111,7 @@ public class DelayWatcher implements Watcher { * 开启处理线程 * * @param event 事件 - * @param currentPath 事件发生的当前Path路径 + * @param key 事件发生的当前WatchKey */ private void startHandleModifyThread(final WatchEvent event, final WatchKey key) { ThreadUtil.execute(() -> { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/IgnoreWatcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/IgnoreWatcher.java deleted file mode 100644 index cc890a1bc..000000000 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/IgnoreWatcher.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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: - * https://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.io.watch.watchers; - -import org.dromara.hutool.core.io.watch.Watcher; - -import java.nio.file.WatchEvent; -import java.nio.file.WatchKey; - -/** - * 跳过所有事件处理Watcher
- * 用户继承此类后实现需要监听的方法 - * - * @author Looly - * @since 3.1.0 - */ -public class IgnoreWatcher implements Watcher { - - @Override - public void onCreate(final WatchEvent event, final WatchKey key) { - } - - @Override - public void onModify(final WatchEvent event, final WatchKey key) { - } - - @Override - public void onDelete(final WatchEvent event, final WatchKey key) { - } - - @Override - public void onOverflow(final WatchEvent event, final WatchKey key) { - } -} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/SimpleWatcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/SimpleWatcher.java index 6607e09d2..1d8d49d56 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/SimpleWatcher.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/watch/watchers/SimpleWatcher.java @@ -12,11 +12,34 @@ package org.dromara.hutool.core.io.watch.watchers; +import org.dromara.hutool.core.io.watch.Watcher; + +import java.io.Serializable; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; + /** * 空白WatchListener
* 用户继承此类后实现需要监听的方法 - * @author Looly * + * @author Looly */ -public class SimpleWatcher extends IgnoreWatcher { +public class SimpleWatcher implements Watcher, Serializable { + private static final long serialVersionUID = 1L; + + @Override + public void onCreate(final WatchEvent event, final WatchKey key) { + } + + @Override + public void onModify(final WatchEvent event, final WatchKey key) { + } + + @Override + public void onDelete(final WatchEvent event, final WatchKey key) { + } + + @Override + public void onOverflow(final WatchEvent event, final WatchKey key) { + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/regex/RegexPool.java b/hutool-core/src/main/java/org/dromara/hutool/core/regex/RegexPool.java index 917d7f546..f0ad97858 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/regex/RegexPool.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/regex/RegexPool.java @@ -171,7 +171,7 @@ public interface RegexPool { * 车架号(车辆识别代号由世界制造厂识别代号(WMI、车辆说明部分(VDS)车辆指示部分(VIS)三部分组成,共 17 位字码。)
* 别名:车辆识别代号、车辆识别码、车架号、十七位码
* 标准号:GB 16735-2019
- * 标准官方地址:https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=E2EBF667F8C032B1EDFD6DF9C1114E02 + * 标准官方地址:https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=E2EBF667F8C032B1EDFD6DF9C1114E02
* 对年产量大于或等于1 000 辆的完整车辆和/或非完整车辆制造厂: *
 	 *   第一部分为世界制造厂识别代号(WMI),3位
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharArray.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharArray.java
new file mode 100644
index 000000000..6b26aef04
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharArray.java
@@ -0,0 +1,105 @@
+package org.dromara.hutool.core.text;
+
+import org.dromara.hutool.core.array.ArrayUtil;
+import org.dromara.hutool.core.collection.iter.ArrayIter;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * char[]包装,提供zero-copy的数组操作
+ *
+ * @author Looly
+ */
+public class CharArray implements CharSequence, Iterable {
+
+	private final char[] value;
+
+	/**
+	 * 构造
+	 *
+	 * @param value String值
+	 */
+	public CharArray(final String value) {
+		this(value.toCharArray(), false);
+	}
+
+	/**
+	 * 构造,注意此方法共享数组
+	 *
+	 * @param value char数组
+	 * @param copy  可选是否拷贝数组,如果为{@code false}则复用数组
+	 */
+	public CharArray(final char[] value, final boolean copy) {
+		this.value = copy ? value.clone() : value;
+	}
+
+	@Override
+	public int length() {
+		return value.length;
+	}
+
+	@Override
+	public char charAt(int index) {
+		if (index < 0) {
+			index += value.length;
+		}
+		return value[index];
+	}
+
+	/**
+	 * 设置字符
+	 *
+	 * @param index 位置,支持复数,-1表示最后一个位置
+	 * @param c     字符
+	 * @return this
+	 */
+	public CharArray set(int index, final char c) {
+		if (index < 0) {
+			index += value.length;
+		}
+		value[index] = c;
+		return this;
+	}
+
+	/**
+	 * 获取原始数组,不做拷贝
+	 *
+	 * @return array
+	 */
+	public char[] array() {
+		return this.value;
+	}
+
+	@Override
+	public CharSequence subSequence(final int start, final int end) {
+		return new CharArray(ArrayUtil.sub(value, start, end), false);
+	}
+
+	@Override
+	public boolean equals(final Object o) {
+		if (this == o) {
+			return true;
+		}
+		if (o == null || getClass() != o.getClass()) {
+			return false;
+		}
+		final CharArray charArray = (CharArray) o;
+		return Arrays.equals(value, charArray.value);
+	}
+
+	@Override
+	public int hashCode() {
+		return Arrays.hashCode(value);
+	}
+
+	@Override
+	public Iterator iterator() {
+		return new ArrayIter<>(this.value);
+	}
+
+	@Override
+	public String toString() {
+		return String.valueOf(this.value);
+	}
+}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java
deleted file mode 100644
index 8956c6260..000000000
--- a/hutool-core/src/test/java/org/dromara/hutool/core/io/WatchMonitorTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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:
- *          https://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.io;
-
-import org.dromara.hutool.core.io.watch.WatchMonitor;
-import org.dromara.hutool.core.io.watch.WatchUtil;
-import org.dromara.hutool.core.io.watch.Watcher;
-import org.dromara.hutool.core.io.watch.watchers.DelayWatcher;
-import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
-import org.dromara.hutool.core.lang.Console;
-
-import java.nio.file.Path;
-import java.nio.file.WatchEvent;
-import java.nio.file.WatchKey;
-
-/**
- * 文件监听单元测试
- *
- * @author Looly
- *
- */
-public class WatchMonitorTest {
-
-	public static void main(final String[] args) {
-		final Watcher watcher = new SimpleWatcher(){
-			@Override
-			public void onCreate(final WatchEvent event, final WatchKey key) {
-				final Object obj = event.context();
-				Console.log(((Path)obj).toAbsolutePath());
-				Console.log("创建:{}-> {}", key.watchable(), obj);
-			}
-
-			@Override
-			public void onModify(final WatchEvent event, final WatchKey key) {
-				final Object obj = event.context();
-				Console.log("修改:{}-> {}", key.watchable(), obj);
-			}
-
-			@Override
-			public void onDelete(final WatchEvent event, final WatchKey key) {
-				final Object obj = event.context();
-				Console.log("删除:{}-> {}", key.watchable(), obj);
-			}
-
-			@Override
-			public void onOverflow(final WatchEvent event, final WatchKey key) {
-				final Object obj = event.context();
-				Console.log("Overflow:{}-> {}", key.watchable(), obj);
-			}
-		};
-
-		//noinspection resource
-		final WatchMonitor monitor = WatchUtil.ofAll("d:/test/aaa.txt", new DelayWatcher(watcher, 500));
-
-		monitor.setMaxDepth(0);
-		monitor.start();
-	}
-
-
-}
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 92fd68891..2e53316dc 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
@@ -13,15 +13,35 @@
 package org.dromara.hutool.core.io.file;
 
 import org.dromara.hutool.core.array.ArrayUtil;
+import org.dromara.hutool.core.lang.Console;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
 
 public class PathUtilTest {
 
+	@SuppressWarnings("DuplicateExpressions")
+	@Test
+	void ofTest() {
+		// 绝对路径测试
+		Path path = PathUtil.of(Paths.get("d:/test/hutool"), Paths.get("data1"), Paths.get("data2"));
+		Assertions.assertEquals("d:/test/hutool/data1/data2", path.toString().replace('\\', '/'));
+
+		// 相对路径测试
+		path = PathUtil.of(Paths.get("hutool"), Paths.get("data1"), Paths.get("data2"));
+		Assertions.assertEquals("hutool/data1/data2", path.toString().replace('\\', '/'));
+
+		path = PathUtil.of(Paths.get("hutool"));
+		Assertions.assertEquals("hutool", path.toString().replace('\\', '/'));
+
+		path = PathUtil.of((Path) null);
+		Assertions.assertNull(path);
+	}
+
 	@Test
 	@Disabled
 	public void copyFileTest(){
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/TestConsoleWatcher.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/TestConsoleWatcher.java
new file mode 100644
index 000000000..25be40ff7
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/TestConsoleWatcher.java
@@ -0,0 +1,35 @@
+package org.dromara.hutool.core.io.watch;
+
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
+import org.dromara.hutool.core.lang.Console;
+
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+
+public class TestConsoleWatcher extends SimpleWatcher {
+	private static final long serialVersionUID = 1L;
+
+	@Override
+	public void onCreate(final WatchEvent event, final WatchKey key) {
+		Console.log("创建:{}-> {}", key.watchable(), event.context());
+		Console.log("Resolved Path:{}", WatchUtil.resolvePath(event, key));
+	}
+
+	@Override
+	public void onModify(final WatchEvent event, final WatchKey key) {
+		Console.log("修改:{}-> {}", key.watchable(), event.context());
+		Console.log("Resolved Path:{}", WatchUtil.resolvePath(event, key));
+	}
+
+	@Override
+	public void onDelete(final WatchEvent event, final WatchKey key) {
+		Console.log("删除:{}-> {}", key.watchable(), event.context());
+		Console.log("Resolved Path:{}", WatchUtil.resolvePath(event, key));
+	}
+
+	@Override
+	public void onOverflow(final WatchEvent event, final WatchKey key) {
+		Console.log("Overflow:{}-> {}", key.watchable(), event.context());
+		Console.log("Resolved Path:{}", WatchUtil.resolvePath(event, key));
+	}
+}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchMonitorTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchMonitorTest.java
new file mode 100644
index 000000000..e7add53d5
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchMonitorTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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:
+ *          https://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.io.watch;
+
+import org.dromara.hutool.core.io.file.PathUtil;
+import org.dromara.hutool.core.io.watch.watchers.DelayWatcher;
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
+import org.dromara.hutool.core.lang.Console;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+
+/**
+ * 文件监听单元测试
+ *
+ * @author Looly
+ */
+public class WatchMonitorTest {
+
+	@Test
+	@Disabled
+	void watchTest() {
+		final Path path = PathUtil.of("d:/test/");
+		Console.log("监听:{}", path);
+
+		//noinspection resource
+		final WatchMonitor monitor = WatchUtil.ofAll(path, new DelayWatcher(new TestConsoleWatcher(), 500));
+		monitor.setMaxDepth(0);
+		monitor.start();
+	}
+}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchServiceWrapperTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchServiceWrapperTest.java
new file mode 100644
index 000000000..337ac13d0
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/io/watch/WatchServiceWrapperTest.java
@@ -0,0 +1,22 @@
+package org.dromara.hutool.core.io.watch;
+
+import org.dromara.hutool.core.io.file.PathUtil;
+import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
+import org.dromara.hutool.core.lang.Console;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+
+public class WatchServiceWrapperTest {
+
+	@SuppressWarnings("resource")
+	@Test
+	@Disabled
+	void watchTest() {
+		WatchServiceWrapper.of(WatchKind.ALL)
+			.registerPath(PathUtil.of("d:/test"), 0)
+			.watch(new TestConsoleWatcher(), null);
+	}
+}