1+ /*
2+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3+ *
4+ * Redistribution and use in source and binary forms, with or without
5+ * modification, are permitted provided that the following conditions
6+ * are met:
7+ *
8+ * - Redistributions of source code must retain the above copyright
9+ * notice, this list of conditions and the following disclaimer.
10+ *
11+ * - Redistributions in binary form must reproduce the above copyright
12+ * notice, this list of conditions and the following disclaimer in the
13+ * documentation and/or other materials provided with the distribution.
14+ *
15+ * - Neither the name of Oracle nor the names of its
16+ * contributors may be used to endorse or promote products derived
17+ * from this software without specific prior written permission.
18+ *
19+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+ */
31+
32+ package processing .app .tools ;
33+
34+ import java .nio .file .*;
35+ import static java .nio .file .StandardWatchEventKinds .*;
36+ import static java .nio .file .LinkOption .*;
37+ import java .nio .file .attribute .*;
38+ import java .io .*;
39+ import java .util .*;
40+ import processing .app .EditorTab ;
41+
42+ /**
43+ * Example to watch a directory (or tree) for changes to files.
44+ */
45+
46+ public class WatchDir {
47+
48+ private final WatchService watcher ;
49+ private final Map <WatchKey ,Path > keys ;
50+ private final boolean recursive ;
51+ private boolean trace = false ;
52+
53+ @ SuppressWarnings ("unchecked" )
54+ static <T > WatchEvent <T > cast (WatchEvent <?> event ) {
55+ return (WatchEvent <T >)event ;
56+ }
57+
58+ /**
59+ * Register the given directory with the WatchService
60+ */
61+ private void register (Path dir ) throws IOException {
62+ WatchKey key = dir .register (watcher , ENTRY_CREATE , ENTRY_DELETE , ENTRY_MODIFY );
63+ if (trace ) {
64+ Path prev = keys .get (key );
65+ if (prev == null ) {
66+ } else {
67+ if (!dir .equals (prev )) {
68+ }
69+ }
70+ }
71+ keys .put (key , dir );
72+ }
73+
74+ /**
75+ * Register the given directory, and all its sub-directories, with the
76+ * WatchService.
77+ */
78+ private void registerAll (final Path start ) throws IOException {
79+ // register directory and sub-directories
80+ Files .walkFileTree (start , new SimpleFileVisitor <Path >() {
81+ @ Override
82+ public FileVisitResult preVisitDirectory (Path dir , BasicFileAttributes attrs )
83+ throws IOException
84+ {
85+ register (dir );
86+ return FileVisitResult .CONTINUE ;
87+ }
88+ });
89+ }
90+
91+ /**
92+ * Creates a WatchService and registers the given directory
93+ */
94+ public WatchDir (Path dir , boolean recursive ) throws IOException {
95+ this .watcher = FileSystems .getDefault ().newWatchService ();
96+ this .keys = new HashMap <WatchKey ,Path >();
97+ this .recursive = recursive ;
98+
99+ if (recursive ) {
100+ registerAll (dir );
101+ } else {
102+ register (dir );
103+ }
104+
105+ // enable trace after initial registration
106+ this .trace = true ;
107+ }
108+
109+ /**
110+ * Process all events for keys queued to the watcher
111+ */
112+ public void processEvents (EditorTab tab ) {
113+ for (;;) {
114+
115+ // wait for key to be signalled
116+ WatchKey key ;
117+ try {
118+ key = watcher .take ();
119+ } catch (InterruptedException x ) {
120+ return ;
121+ }
122+
123+ Path dir = keys .get (key );
124+ if (dir == null ) {
125+ continue ;
126+ }
127+
128+ for (WatchEvent <?> event : key .pollEvents ()) {
129+ WatchEvent .Kind kind = event .kind ();
130+
131+ // TBD - provide example of how OVERFLOW event is handled
132+ if (kind == OVERFLOW ) {
133+ continue ;
134+ }
135+
136+ // Context for directory entry event is the file name of entry
137+ WatchEvent <Path > ev = cast (event );
138+ Path name = ev .context ();
139+ Path child = dir .resolve (name );
140+
141+ // reload the tab content
142+ tab .reload ();
143+
144+ // if directory is created, and watching recursively, then
145+ // register it and its sub-directories
146+ if (recursive && (kind == ENTRY_CREATE )) {
147+ try {
148+ if (Files .isDirectory (child , NOFOLLOW_LINKS )) {
149+ registerAll (child );
150+ }
151+ } catch (IOException x ) {
152+ // ignore to keep sample readbale
153+ }
154+ }
155+ }
156+
157+ // reset key and remove from set if directory no longer accessible
158+ boolean valid = key .reset ();
159+ if (!valid ) {
160+ keys .remove (key );
161+
162+ // all directories are inaccessible
163+ if (keys .isEmpty ()) {
164+ break ;
165+ }
166+ }
167+ }
168+ }
169+ }
0 commit comments