add method

This commit is contained in:
Looly 2019-10-05 20:54:41 +08:00
parent 90cd958e37
commit 52fb6267a5
6 changed files with 264 additions and 163 deletions

View File

@ -11,6 +11,7 @@
* 【core】 ClassScanner支持自定义ClassLoader * 【core】 ClassScanner支持自定义ClassLoader
* 【core】 修改错别字pr#568@Github * 【core】 修改错别字pr#568@Github
* 【core】 增加DateUtil.parseCST方法issue#570@Github * 【core】 增加DateUtil.parseCST方法issue#570@Github
* 【core】 增加defaultIfEmpty方法
### Bug修复 ### Bug修复
* 【all】 修复阶乘计算错误bugissue#I12XE4@Gitee * 【all】 修复阶乘计算错误bugissue#I12XE4@Gitee

View File

@ -1389,6 +1389,20 @@ public class CollUtil {
return collection == null || collection.isEmpty(); return collection == null || collection.isEmpty();
} }
/**
* 如果给定集合为空返回默认集合
*
* @param <T> 集合类型
* @param <E> 集合元素类型
* @param collection 集合
* @param defaultCollection 默认数组
* @return 非空empty的原集合或默认集合
* @since 4.6.9
*/
public static <T extends Collection<E>, E> T defaultIfEmpty(T collection, T defaultCollection){
return isEmpty(collection) ? defaultCollection : collection;
}
/** /**
* Map是否为空 * Map是否为空
* *

View File

@ -34,245 +34,293 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
/** /**
* 路径监听器<br> * 路径监听器
*
* <p>
* 监听器可监听目录或文件<br> * 监听器可监听目录或文件<br>
* 如果监听的Path不存在则递归创建空目录然后监听此空目录<br> * 如果监听的Path不存在则递归创建空目录然后监听此空目录<br>
* 递归监听目录时并不会监听新创建的目录 * 递归监听目录时并不会监听新创建的目录
*
* @author Looly
* *
* @author Looly
*/ */
public class WatchMonitor extends Thread implements Closeable, Serializable{ public class WatchMonitor extends Thread implements Closeable, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 事件丢失 */ /**
* 事件丢失
*/
public static final WatchEvent.Kind<?> OVERFLOW = StandardWatchEventKinds.OVERFLOW; public static final WatchEvent.Kind<?> OVERFLOW = StandardWatchEventKinds.OVERFLOW;
/** 修改事件 */ /**
* 修改事件
*/
public static final WatchEvent.Kind<?> ENTRY_MODIFY = StandardWatchEventKinds.ENTRY_MODIFY; public static final WatchEvent.Kind<?> ENTRY_MODIFY = StandardWatchEventKinds.ENTRY_MODIFY;
/** 创建事件 */ /**
* 创建事件
*/
public static final WatchEvent.Kind<?> ENTRY_CREATE = StandardWatchEventKinds.ENTRY_CREATE; public static final WatchEvent.Kind<?> ENTRY_CREATE = StandardWatchEventKinds.ENTRY_CREATE;
/** 删除事件 */ /**
* 删除事件
*/
public static final WatchEvent.Kind<?> ENTRY_DELETE = StandardWatchEventKinds.ENTRY_DELETE; public static final WatchEvent.Kind<?> ENTRY_DELETE = StandardWatchEventKinds.ENTRY_DELETE;
/** 全部事件 */ /**
* 全部事件
*/
public static final WatchEvent.Kind<?>[] EVENTS_ALL = {// public static final WatchEvent.Kind<?>[] EVENTS_ALL = {//
OVERFLOW, //事件丢失 OVERFLOW, //事件丢失
ENTRY_MODIFY, //修改 ENTRY_MODIFY, //修改
ENTRY_CREATE, //创建 ENTRY_CREATE, //创建
ENTRY_DELETE //删除 ENTRY_DELETE //删除
}; };
/** 监听路径,必须为目录 */ /**
* 监听路径必须为目录
*/
private Path path; private Path path;
/** 递归目录的最大深度当小于1时不递归下层目录 */ /**
* 递归目录的最大深度当小于1时不递归下层目录
*/
private int maxDepth; private int maxDepth;
/** 监听的文件,对于单文件监听不为空 */ /**
* 监听的文件对于单文件监听不为空
*/
private Path filePath; private Path filePath;
/** 监听服务 */ /**
* 监听服务
*/
private WatchService watchService; private WatchService watchService;
/** 监听器 */ /**
* 监听器
*/
private Watcher watcher; private Watcher watcher;
/** 监听事件列表 */ /**
* 监听事件列表
*/
private WatchEvent.Kind<?>[] events; private WatchEvent.Kind<?>[] events;
/** 监听是否已经关闭 */ /**
* 监听是否已经关闭
*/
private boolean isClosed; private boolean isClosed;
/** WatchKey 和 Path的对应表 */ /**
* WatchKey Path的对应表
*/
private Map<WatchKey, Path> watchKeyPathMap = new HashMap<>(); private Map<WatchKey, Path> watchKeyPathMap = new HashMap<>();
//------------------------------------------------------ Static method start //------------------------------------------------------ Static method start
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param url URL *
* @param url URL
* @param events 监听的事件列表 * @param events 监听的事件列表
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(URL url, WatchEvent.Kind<?>... events){ public static WatchMonitor create(URL url, WatchEvent.Kind<?>... events) {
return create(url, 0, events); return create(url, 0, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param url URL *
* @param events 监听的事件列表 * @param url URL
* @param events 监听的事件列表
* @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录 * @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(URL url, int maxDepth, WatchEvent.Kind<?>... events){ public static WatchMonitor create(URL url, int maxDepth, WatchEvent.Kind<?>... events) {
return create(URLUtil.toURI(url), maxDepth, events); return create(URLUtil.toURI(url), maxDepth, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param uri URI *
* @param uri URI
* @param events 监听的事件列表 * @param events 监听的事件列表
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(URI uri, WatchEvent.Kind<?>... events){ public static WatchMonitor create(URI uri, WatchEvent.Kind<?>... events) {
return create(uri, 0, events); return create(uri, 0, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param uri URI *
* @param events 监听的事件列表 * @param uri URI
* @param events 监听的事件列表
* @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录 * @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(URI uri, int maxDepth, WatchEvent.Kind<?>... events){ public static WatchMonitor create(URI uri, int maxDepth, WatchEvent.Kind<?>... events) {
return create(Paths.get(uri), maxDepth, events); return create(Paths.get(uri), maxDepth, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param file 文件 *
* @param file 文件
* @param events 监听的事件列表 * @param events 监听的事件列表
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(File file, WatchEvent.Kind<?>... events){ public static WatchMonitor create(File file, WatchEvent.Kind<?>... events) {
return create(file, 0, events); return create(file, 0, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param file 文件 *
* @param events 监听的事件列表 * @param file 文件
* @param events 监听的事件列表
* @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录 * @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(File file, int maxDepth, WatchEvent.Kind<?>... events){ public static WatchMonitor create(File file, int maxDepth, WatchEvent.Kind<?>... events) {
return create(file.toPath(), maxDepth, events); return create(file.toPath(), maxDepth, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param path 路径 *
* @param path 路径
* @param events 监听的事件列表 * @param events 监听的事件列表
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(String path, WatchEvent.Kind<?>... events){ public static WatchMonitor create(String path, WatchEvent.Kind<?>... events) {
return create(path, 0, events); return create(path, 0, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param path 路径 *
* @param events 监听的事件列表 * @param path 路径
* @param events 监听的事件列表
* @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录 * @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(String path, int maxDepth, WatchEvent.Kind<?>... events){ public static WatchMonitor create(String path, int maxDepth, WatchEvent.Kind<?>... events) {
return create(Paths.get(path), maxDepth, events); return create(Paths.get(path), maxDepth, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param path 路径 *
* @param path 路径
* @param events 监听事件列表 * @param events 监听事件列表
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(Path path, WatchEvent.Kind<?>... events){ public static WatchMonitor create(Path path, WatchEvent.Kind<?>... events) {
return create(path, 0, events); return create(path, 0, events);
} }
/** /**
* 创建并初始化监听 * 创建并初始化监听
* @param path 路径 *
* @param events 监听事件列表 * @param path 路径
* @param events 监听事件列表
* @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录 * @param maxDepth 当监听目录时监听目录的最大深度当设置值为1或小于1表示不递归监听子目录
* @return 监听对象 * @return 监听对象
*/ */
public static WatchMonitor create(Path path, int maxDepth, WatchEvent.Kind<?>... events){ public static WatchMonitor create(Path path, int maxDepth, WatchEvent.Kind<?>... events) {
return new WatchMonitor(path, maxDepth, events); return new WatchMonitor(path, maxDepth, events);
} }
//--------- createAll //--------- createAll
/** /**
* 创建并初始化监听监听所有事件 * 创建并初始化监听监听所有事件
* @param uri URI *
* @param uri URI
* @param watcher {@link Watcher} * @param watcher {@link Watcher}
* @return {@link WatchMonitor} * @return {@link WatchMonitor}
*/ */
public static WatchMonitor createAll(URI uri, Watcher watcher){ public static WatchMonitor createAll(URI uri, Watcher watcher) {
return createAll(Paths.get(uri), watcher); return createAll(Paths.get(uri), watcher);
} }
/** /**
* 创建并初始化监听监听所有事件 * 创建并初始化监听监听所有事件
* @param url URL *
* @param url URL
* @param watcher {@link Watcher} * @param watcher {@link Watcher}
* @return {@link WatchMonitor} * @return {@link WatchMonitor}
*/ */
public static WatchMonitor createAll(URL url, Watcher watcher){ public static WatchMonitor createAll(URL url, Watcher watcher) {
try { try {
return createAll(Paths.get(url.toURI()), watcher); return createAll(Paths.get(url.toURI()), watcher);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
throw new WatchException(e); throw new WatchException(e);
} }
} }
/** /**
* 创建并初始化监听监听所有事件 * 创建并初始化监听监听所有事件
* @param file 被监听文件 *
* @param file 被监听文件
* @param watcher {@link Watcher} * @param watcher {@link Watcher}
* @return {@link WatchMonitor} * @return {@link WatchMonitor}
*/ */
public static WatchMonitor createAll(File file, Watcher watcher){ public static WatchMonitor createAll(File file, Watcher watcher) {
return createAll(file.toPath(), watcher); return createAll(file.toPath(), watcher);
} }
/** /**
* 创建并初始化监听监听所有事件 * 创建并初始化监听监听所有事件
* @param path 路径 *
* @param path 路径
* @param watcher {@link Watcher} * @param watcher {@link Watcher}
* @return {@link WatchMonitor} * @return {@link WatchMonitor}
*/ */
public static WatchMonitor createAll(String path, Watcher watcher){ public static WatchMonitor createAll(String path, Watcher watcher) {
return createAll(Paths.get(path), watcher); return createAll(Paths.get(path), watcher);
} }
/** /**
* 创建并初始化监听监听所有事件 * 创建并初始化监听监听所有事件
* @param path 路径 *
* @param path 路径
* @param watcher {@link Watcher} * @param watcher {@link Watcher}
* @return {@link WatchMonitor} * @return {@link WatchMonitor}
*/ */
public static WatchMonitor createAll(Path path, Watcher watcher){ public static WatchMonitor createAll(Path path, Watcher watcher) {
final WatchMonitor watchMonitor = create(path, EVENTS_ALL); final WatchMonitor watchMonitor = create(path, EVENTS_ALL);
watchMonitor.setWatcher(watcher); watchMonitor.setWatcher(watcher);
return watchMonitor; return watchMonitor;
} }
//------------------------------------------------------ Static method end //------------------------------------------------------ Static method end
//------------------------------------------------------ Constructor method start //------------------------------------------------------ Constructor method start
/** /**
* 构造 * 构造
* @param file 文件 *
* @param file 文件
* @param events 监听的事件列表 * @param events 监听的事件列表
*/ */
public WatchMonitor(File file, WatchEvent.Kind<?>... events) { public WatchMonitor(File file, WatchEvent.Kind<?>... events) {
this(file.toPath(), events); this(file.toPath(), events);
} }
/** /**
* 构造 * 构造
* @param path 字符串路径 *
* @param path 字符串路径
* @param events 监听的事件列表 * @param events 监听的事件列表
*/ */
public WatchMonitor(String path, WatchEvent.Kind<?>... events) { public WatchMonitor(String path, WatchEvent.Kind<?>... events) {
this(Paths.get(path), events); this(Paths.get(path), events);
} }
/** /**
* 构造 * 构造
* @param path 字符串路径 *
* @param path 字符串路径
* @param events 监听事件列表 * @param events 监听事件列表
*/ */
public WatchMonitor(Path path, WatchEvent.Kind<?>... events) { public WatchMonitor(Path path, WatchEvent.Kind<?>... events) {
this(path, 0, events); this(path, 0, events);
} }
/** /**
* 构造<br> * 构造<br>
* 例如设置 * 例如设置
@ -281,10 +329,10 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
* maxDepth = 2 表示监听当前目录以及下层目录 * maxDepth = 2 表示监听当前目录以及下层目录
* maxDepth = 3 表示监听当前目录以及下层 * maxDepth = 3 表示监听当前目录以及下层
* </pre> * </pre>
* *
* @param path 字符串路径 * @param path 字符串路径
* @param maxDepth 递归目录的最大深度当小于2时不递归下层目录 * @param maxDepth 递归目录的最大深度当小于2时不递归下层目录
* @param events 监听事件列表 * @param events 监听事件列表
*/ */
public WatchMonitor(Path path, int maxDepth, WatchEvent.Kind<?>... events) { public WatchMonitor(Path path, int maxDepth, WatchEvent.Kind<?>... events) {
this.path = path; this.path = path;
@ -293,7 +341,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
this.init(); this.init();
} }
//------------------------------------------------------ Constructor method end //------------------------------------------------------ Constructor method end
/** /**
* 初始化<br> * 初始化<br>
* 初始化包括 * 初始化包括
@ -301,111 +349,89 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
* 1解析传入的路径判断其为目录还是文件 * 1解析传入的路径判断其为目录还是文件
* 2创建{@link WatchService} 对象 * 2创建{@link WatchService} 对象
* </pre> * </pre>
* *
* @throws WatchException 监听异常IO异常时抛出此异常 * @throws WatchException 监听异常IO异常时抛出此异常
*/ */
public void init() throws WatchException{ public void init() throws WatchException {
//获取目录或文件路径 //获取目录或文件路径
if(false ==Files.exists(this.path, LinkOption.NOFOLLOW_LINKS)) { if (false == Files.exists(this.path, LinkOption.NOFOLLOW_LINKS)) {
// 不存在的路径
final Path lastPathEle = FileUtil.getLastPathEle(this.path); final Path lastPathEle = FileUtil.getLastPathEle(this.path);
if(null != lastPathEle) { if (null != lastPathEle) {
final String lastPathEleStr = lastPathEle.toString(); final String lastPathEleStr = lastPathEle.toString();
//带有点表示有扩展名按照未创建的文件对待Linux下.d的为目录排除之 //带有点表示有扩展名按照未创建的文件对待Linux下.d的为目录排除之
if(StrUtil.contains(lastPathEleStr, StrUtil.C_DOT) && false ==StrUtil.endWithIgnoreCase(lastPathEleStr, ".d")) { if (StrUtil.contains(lastPathEleStr, StrUtil.C_DOT) && false == StrUtil.endWithIgnoreCase(lastPathEleStr, ".d")) {
this.filePath = this.path; this.filePath = this.path;
this.path = this.filePath.getParent(); this.path = this.filePath.getParent();
} }
} }
//创建不存在的目录或父目录 //创建不存在的目录或父目录
try { try {
Files.createDirectories(this.path); Files.createDirectories(this.path);
} catch (IOException e) { } catch (IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }
}else if(Files.isRegularFile(this.path, LinkOption.NOFOLLOW_LINKS)){ } else if (Files.isRegularFile(this.path, LinkOption.NOFOLLOW_LINKS)) {
// 文件路径
this.filePath = this.path; this.filePath = this.path;
this.path = this.filePath.getParent(); this.path = this.filePath.getParent();
} }
//初始化监听 //初始化监听
try { try {
watchService = FileSystems.getDefault().newWatchService(); watchService = FileSystems.getDefault().newWatchService();
} catch (IOException e) { } catch (IOException e) {
throw new WatchException(e); throw new WatchException(e);
} }
isClosed = false; isClosed = false;
} }
/** /**
* 设置监听<br> * 设置监听<br>
* 多个监听请使用{@link WatcherChain} * 多个监听请使用{@link WatcherChain}
* *
* @param watcher 监听 * @param watcher 监听
* @return {@link WatchMonitor} * @return {@link WatchMonitor}
*/ */
public WatchMonitor setWatcher(Watcher watcher){ public WatchMonitor setWatcher(Watcher watcher) {
this.watcher = watcher; this.watcher = watcher;
return this; return this;
} }
@Override @Override
public void run() { public void run() {
watch(); watch();
} }
/** /**
* 开始监听事件阻塞当前进程 * 开始监听事件阻塞当前进程
*/ */
public void watch(){ public void watch() {
watch(this.watcher); watch(this.watcher);
} }
/** /**
* 开始监听事件阻塞当前进程 * 开始监听事件阻塞当前进程
*
* @param watcher 监听 * @param watcher 监听
* @throws WatchException 监听异常如果监听关闭抛出此异常 * @throws WatchException 监听异常如果监听关闭抛出此异常
*/ */
public void watch(Watcher watcher) throws WatchException{ public void watch(Watcher watcher) throws WatchException {
if(isClosed){ if (isClosed) {
throw new WatchException("Watch Monitor is closed !"); throw new WatchException("Watch Monitor is closed !");
} }
// 按照层级注册路径及其子路径
registerPath(); registerPath();
// log.debug("Start watching path: [{}]", this.path); // log.debug("Start watching path: [{}]", this.path);
while (false == isClosed) { while (false == isClosed) {
WatchKey wk; doTakeAndWatch(watcher);
try {
wk = watchService.take();
} catch (InterruptedException | ClosedWatchServiceException e) {
// log.warn(e);
return;
}
final Path currentPath = watchKeyPathMap.get(wk);
WatchEvent.Kind<?> kind;
for (WatchEvent<?> event : wk.pollEvents()) {
kind = event.kind();
if(null != this.filePath && false == this.filePath.endsWith(event.context().toString())){
// log.debug("[{}] is not fit for [{}], pass it.", event.context(), this.filePath.getFileName());
continue;
}
if(kind == StandardWatchEventKinds.ENTRY_CREATE){
watcher.onCreate(event, currentPath);
}else if(kind == StandardWatchEventKinds.ENTRY_MODIFY){
watcher.onModify(event, currentPath);
}else if(kind == StandardWatchEventKinds.ENTRY_DELETE){
watcher.onDelete(event, currentPath);
}else if(kind == StandardWatchEventKinds.OVERFLOW){
watcher.onOverflow(event, currentPath);
}
}
wk.reset();
} }
} }
/** /**
* 当监听目录时监听目录的最大深度<br> * 当监听目录时监听目录的最大深度<br>
* 当设置值为1或小于1表示不递归监听子目录<br> * 当设置值为1或小于1表示不递归监听子目录<br>
@ -415,7 +441,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
* maxDepth = 2 表示监听当前目录以及下层目录 * maxDepth = 2 表示监听当前目录以及下层目录
* maxDepth = 3 表示监听当前目录以及下层 * maxDepth = 3 表示监听当前目录以及下层
* </pre> * </pre>
* *
* @param maxDepth 最大深度当设置值为1或小于1表示不递归监听子目录监听所有子目录请传{@link Integer#MAX_VALUE} * @param maxDepth 最大深度当设置值为1或小于1表示不递归监听子目录监听所有子目录请传{@link Integer#MAX_VALUE}
* @return this * @return this
*/ */
@ -423,37 +449,76 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
this.maxDepth = maxDepth; this.maxDepth = maxDepth;
return this; return this;
} }
/** /**
* 关闭监听 * 关闭监听
*/ */
@Override @Override
public void close(){ public void close() {
isClosed = true; isClosed = true;
IoUtil.close(watchService); IoUtil.close(watchService);
} }
//------------------------------------------------------ private method start //------------------------------------------------------ private method start
/**
* 执行事件获取并处理
*
* @param watcher {@link Watcher}
*/
private void doTakeAndWatch(Watcher watcher){
WatchKey wk;
try {
wk = watchService.take();
} catch (InterruptedException | ClosedWatchServiceException e) {
// 用户中断
return;
}
final Path currentPath = watchKeyPathMap.get(wk);
WatchEvent.Kind<?> kind;
for (WatchEvent<?> event : wk.pollEvents()) {
kind = event.kind();
// 如果监听文件检查当前事件是否与所监听文件关联
if (null != this.filePath && false == this.filePath.endsWith(event.context().toString())) {
// log.debug("[{}] is not fit for [{}], pass it.", event.context(), this.filePath.getFileName());
continue;
}
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
watcher.onCreate(event, currentPath);
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
watcher.onModify(event, currentPath);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
watcher.onDelete(event, currentPath);
} else if (kind == StandardWatchEventKinds.OVERFLOW) {
watcher.onOverflow(event, currentPath);
}
}
wk.reset();
}
/** /**
* 注册监听路径 * 注册监听路径
*/ */
private void registerPath() { private void registerPath() {
registerPath(this.path, (null != this.filePath) ? 0 : this.maxDepth); registerPath(this.path, (null != this.filePath) ? 0 : this.maxDepth);
} }
/** /**
* 将指定路径加入到监听中 * 将指定路径加入到监听中
* @param path 路径 *
* @param path 路径
* @param maxDepth 递归下层目录的最大深度 * @param maxDepth 递归下层目录的最大深度
* @return {@link WatchKey}
*/ */
private void registerPath(Path path, int maxDepth) { private void registerPath(Path path, int maxDepth) {
try { try {
final WatchKey key = path.register(watchService, ArrayUtil.isEmpty(this.events) ? EVENTS_ALL : this.events); final WatchKey key = path.register(this.watchService, ArrayUtil.defaultIfEmpty(this.events, EVENTS_ALL));
watchKeyPathMap.put(key, path); watchKeyPathMap.put(key, path);
if(maxDepth > 1) { if (maxDepth > 1) {
//遍历所有子目录并加入监听 //遍历所有子目录并加入监听
Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), maxDepth, new SimpleFileVisitor<Path>(){ Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), maxDepth, new SimpleFileVisitor<Path>() {
@Override @Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
registerPath(dir, 0);//继续添加目录 registerPath(dir, 0);//继续添加目录
@ -462,7 +527,7 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
}); });
} }
} catch (IOException e) { } catch (IOException e) {
if(e instanceof AccessDeniedException) { if (e instanceof AccessDeniedException) {
//对于禁止访问的目录跳过监听 //对于禁止访问的目录跳过监听
return; return;
} }

View File

@ -5,34 +5,39 @@ import java.nio.file.WatchEvent;
/** /**
* 观察者监视器 * 观察者监视器
*
* @author Looly * @author Looly
*/ */
public interface Watcher { public interface Watcher {
/** /**
* 文件创建时执行的方法 * 文件创建时执行的方法
* @param event 事件 *
* @param event 事件
* @param currentPath 事件发生的当前Path路径 * @param currentPath 事件发生的当前Path路径
*/ */
void onCreate(WatchEvent<?> event, Path currentPath); void onCreate(WatchEvent<?> event, Path currentPath);
/** /**
* 文件修改时执行的方法<br> * 文件修改时执行的方法<br>
* 文件修改可能触发多次 * 文件修改可能触发多次
* @param event 事件 *
* @param event 事件
* @param currentPath 事件发生的当前Path路径 * @param currentPath 事件发生的当前Path路径
*/ */
void onModify(WatchEvent<?> event, Path currentPath); void onModify(WatchEvent<?> event, Path currentPath);
/** /**
* 文件删除时执行的方法 * 文件删除时执行的方法
* @param event 事件 *
* @param event 事件
* @param currentPath 事件发生的当前Path路径 * @param currentPath 事件发生的当前Path路径
*/ */
void onDelete(WatchEvent<?> event, Path currentPath); void onDelete(WatchEvent<?> event, Path currentPath);
/** /**
* 事件丢失或出错时执行的方法 * 事件丢失或出错时执行的方法
* @param event 事件 *
* @param event 事件
* @param currentPath 事件发生的当前Path路径 * @param currentPath 事件发生的当前Path路径
*/ */
void onOverflow(WatchEvent<?> event, Path currentPath); void onOverflow(WatchEvent<?> event, Path currentPath);

View File

@ -1,19 +1,7 @@
package cn.hutool.core.map; package cn.hutool.core.map;
import java.util.AbstractMap; import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
@ -73,6 +61,21 @@ public class MapUtil {
return (null == set) ? Collections.<K, V>emptyMap() : set; return (null == set) ? Collections.<K, V>emptyMap() : set;
} }
/**
* 如果给定Map为空返回默认Map
*
* @param <T> 集合类型
* @param <K> 键类型
* @param <V> 值类型
* @param map Map
* @param defaultMap 默认Map
* @return 非空empty的原Map或默认Map
* @since 4.6.9
*/
public static <T extends Map<K, V>, K, V> T defaultIfEmpty(T map, T defaultMap){
return isEmpty(map) ? defaultMap : map;
}
// ----------------------------------------------------------------------------------------------- new HashMap // ----------------------------------------------------------------------------------------------- new HashMap
/** /**
* 新建一个HashMap * 新建一个HashMap

View File

@ -33,6 +33,19 @@ public class ArrayUtil {
return array == null || array.length == 0; return array == null || array.length == 0;
} }
/**
* 如果给定数组为空返回默认数组
*
* @param <T> 数组元素类型
* @param array 数组
* @param defaultArray 默认数组
* @return 非空empty的原数组或默认数组
* @since 4.6.9
*/
public static <T> T[] defaultIfEmpty(T[] array, T[] defaultArray){
return isEmpty(array) ? defaultArray : array;
}
/** /**
* 数组是否为空<br> * 数组是否为空<br>
* 此方法会匹配单一对象如果此对象为{@code null}则返回true<br> * 此方法会匹配单一对象如果此对象为{@code null}则返回true<br>