/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jooby.filewatcher; import com.sun.nio.file.SensitivityWatchEventModifier; import java.io.IOException; import java.nio.file.*; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.function.Function; import static java.nio.file.StandardWatchEventKinds.*; import static java.util.Objects.*; /** * Allow to customize a file watch handler. You can listen for particular event kinds, apply a glob * filter, recursive listening for changes and set watcher priority or modifier. * * Default filter watch recursively listen for all the available kinds using a <code>HIGH</code> * modifier. * * This class can't be instantiated by client code. Intances of this class are provided via * configuration callbacks. See * {@link FileWatcher#register(Path, Class, java.util.function.Consumer)} * * @author edgar * @since 1.1.0 */ public class FileEventOptions { private List<WatchEvent.Kind<Path>> kinds = new ArrayList<>(); static final PathMatcher TRUE = new PathMatcher() { @Override public boolean matches(final Path path) { return true; } @Override public String toString() { return "**/*"; } }; private static final WatchEvent.Kind<?>[] KINDS = {ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}; private final List<PathMatcher> matchers = new ArrayList<>(); private Modifier modifier = SensitivityWatchEventModifier.HIGH; private boolean recursive = true; private final Class<? extends FileEventHandler> handler; private final Path path; private FileEventHandler handlerInstance; FileEventOptions(final Path path, final Class<? extends FileEventHandler> handler) throws IOException { if (!Files.exists(path)) { Files.createDirectories(path); } this.path = path; this.handler = handler; this.handlerInstance = null; } FileEventOptions(final Path path, final FileEventHandler handler) throws IOException { this(path, handler.getClass()); this.handlerInstance = handler; } /** * Append a kind filter. * * The default filter is: {@link StandardWatchEventKinds#ENTRY_CREATE}, * {@link StandardWatchEventKinds#ENTRY_DELETE} and {@link StandardWatchEventKinds#ENTRY_MODIFY}. * * @param kind Filter type. * @return This options. */ public FileEventOptions kind(final WatchEvent.Kind<Path> kind) { requireNonNull(kind, "WatchEvent.Kind required."); kinds.add(kind); return this; } /** * Turn on/off watching of subfolder. * * @param recursive True to watch on subfolder. * @return This options. */ public FileEventOptions recursive(final boolean recursive) { this.recursive = recursive; return this; } /** * Add a path filter using <code>glob</code> expression, like <code>**/*.java</code>, * etc... * * @param expression Glob expression. * @return This options. */ public FileEventOptions includes(final String expression) { requireNonNull(expression, "Glob expression required."); this.matchers.add(new GlobPathMatcher(expression)); return this; } /** * Set a watch modifier. Default is: <code>HIGH</code>. * * @param modifier Watch modifier. * @return This options. */ public FileEventOptions modifier(final WatchEvent.Modifier modifier) { requireNonNull(modifier, "Modifier required."); this.modifier = modifier; return this; } Modifier modifier() { return modifier; } WatchEvent.Kind<?>[] kinds() { return kinds.size() == 0 ? KINDS : kinds.toArray(new WatchEvent.Kind[0]); } PathMatcher filter() { return matchers.size() == 0 ? TRUE : new FirstOfPathMatcher(matchers); } boolean recursive() { return recursive; } Path path() { return path; } FileEventHandler handler( final Function<Class<? extends FileEventHandler>, FileEventHandler> factory) { return Optional.ofNullable(handlerInstance).orElseGet(() -> factory.apply(handler)); } @Override public String toString() { return path + " {kinds: " + Arrays.toString(kinds()) + ", filter: " + filter() + ", recursive: " + recursive + ", modifier: " + modifier + "}"; } }