This commit is contained in:
Looly 2023-12-29 20:21:40 +08:00
parent 03b8f57648
commit 91178e45a8
15 changed files with 442 additions and 198 deletions

View File

@ -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路径转换为标准的绝对路径<br>
* 如果{@link Path#isAbsolute()}{@code true}表示已经是绝对路径返回本身<br>
* 如果是相对路径则返回相对于系统默认目录的路径一般为项目路径
*
* @param path 文件或目录Path
* @return 转换后的Path
@ -594,7 +634,10 @@ public class PathUtil {
}
/**
* 获取实际路径路径文件必须存在
* 获取实际路径路径文件必须存在<br>
* 如果给定Path是软链接符号链接则返回指向的实际链接<br>
* 如果路径不存在会直接抛出NoSuchFileException异常<br>
* 无论给定是何种类型的路径返回都是唯一的路径总是equals
*
* @param path 路径
* @return 实际路径

View File

@ -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 {
* 初始化包括
* <pre>
* 1解析传入的路径判断其为目录还是文件
* 2创建{@link WatchService} 对象
* </pre>
*
* @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()));
}
/**

View File

@ -25,8 +25,11 @@ import java.util.function.BiConsumer;
import java.util.function.Predicate;
/**
* {@link WatchEvent} 包装类提供可选的监听事件和监听选项<br>
* 通过多次调用{@link #registerPath(Path, int)} 可以递归注册多个路径
* {@link WatchEvent} 包装类提供可选的监听事件和监听选项实现方法包括
* <ul>
* <li>注册{@link #registerPath(Path, int)}注册需要监听的路径</li>
* <li>监听{@link #watch(Watcher, Predicate)} 启动监听并指定事件触发后的行为</li>
* </ul>
*
* @author looly
* @since 6.0.0
@ -34,12 +37,22 @@ import java.util.function.Predicate;
public class WatchServiceWrapper implements WatchService, Wrapper<WatchService>, 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<WatchService>,
/**
* 构造
*
* @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<WatchService>,
throw new WatchException(e);
}
isClosed = false;
this.events = events;
}
@Override
@ -148,6 +163,8 @@ public class WatchServiceWrapper implements WatchService, Wrapper<WatchService>,
*
* @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<WatchService>,
*
* @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<Path>() {
@SuppressWarnings("resource")
@Override
public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
//继续添加目录
@ -192,10 +210,14 @@ public class WatchServiceWrapper implements WatchService, Wrapper<WatchService>,
return super.postVisitDirectory(dir, exc);
}
});
return this;
}
/**
* 执行事件获取并处理
* 执行事件获取并处理<br>
* {@link WatchEvent#context()}是实际操作的文件或目录的相对监听路径的Path非绝对路径<br>
* {@link WatchKey#watchable()}是监听的Path<br>
* 此方法调用后阻塞线程直到触发监听事件执行后退出无循环执行操作
*
* @param watcher {@link Watcher}
* @param watchFilter 监听过滤接口通过实现此接口过滤掉不需要监听的情况{@link Predicate#test(Object)}{@code true}保留null表示不过滤
@ -217,7 +239,22 @@ public class WatchServiceWrapper implements WatchService, Wrapper<WatchService>,
}
/**
* 执行事件获取并处理
* 执行事件获取并处理<br>
* {@link WatchEvent#context()}是实际操作的文件或目录的相对监听路径的Path非绝对路径<br>
* {@link WatchKey#watchable()}是监听的Path<br>
* 此方法调用后阻塞线程直到触发监听事件执行后退出无循环执行操作
*
* @param action 监听回调函数实现此函数接口用于处理WatchEvent事件
*/
public void watch(final BiConsumer<WatchEvent<?>, WatchKey> action) {
watch(action, null);
}
/**
* 执行事件获取并处理<br>
* {@link WatchEvent#context()}是实际操作的文件或目录的相对监听路径的Path非绝对路径<br>
* {@link WatchKey#watchable()}是监听的Path<br>
* 此方法调用后阻塞线程直到触发监听事件执行后退出无循环执行操作
*
* @param action 监听回调函数实现此函数接口用于处理WatchEvent事件
* @param watchFilter 监听过滤接口通过实现此接口过滤掉不需要监听的情况{@link Predicate#test(Object)}{@code true}保留null表示不过滤

View File

@ -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());
}
}

View File

@ -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 {
* 文件修改时执行的方法<br>
* 文件修改可能触发多次
*
* @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);

View File

@ -32,22 +32,30 @@ import java.util.Set;
*/
public class DelayWatcher implements Watcher {
/** Path集合。此集合用于去重在指定delay内多次触发的文件Path */
/**
* Path集合此集合用于去重在指定delay内多次触发的文件Path
*/
private final Set<Path> 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(() -> {

View File

@ -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<br>
* 用户继承此类后实现需要监听的方法
*
* @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) {
}
}

View File

@ -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<br>
* 用户继承此类后实现需要监听的方法
* @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) {
}
}

View File

@ -171,7 +171,7 @@ public interface RegexPool {
* 车架号车辆识别代号由世界制造厂识别代号(WMI车辆说明部分(VDS)车辆指示部分(VIS)三部分组成 17 位字码<br>
* 别名车辆识别代号车辆识别码车架号十七位码<br>
* 标准号GB 16735-2019<br>
* 标准官方地址https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=E2EBF667F8C032B1EDFD6DF9C1114E02
* 标准官方地址https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=E2EBF667F8C032B1EDFD6DF9C1114E02<br>
* 对年产量大于或等于1 000 辆的完整车辆和/或非完整车辆制造厂
* <pre>
* 第一部分为世界制造厂识别代号(WMI)3位

View File

@ -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<Character> {
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<Character> iterator() {
return new ArrayIter<>(this.value);
}
@Override
public String toString() {
return String.valueOf(this.value);
}
}

View File

@ -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();
}
}

View File

@ -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(){

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}