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} 包装类,提供可选的监听事件和监听选项
* 第一部分为世界制造厂识别代号(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); + } +}