|
37 | 37 |
|
38 | 38 | import org.apache.logging.log4j.LogManager; |
39 | 39 | import org.apache.logging.log4j.Logger; |
40 | | -import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; |
41 | 40 |
|
42 | 41 | import engineering.swat.watch.WatchEvent; |
43 | 42 | import engineering.swat.watch.WatchScope; |
44 | 43 | import engineering.swat.watch.impl.EventHandlingWatch; |
45 | 44 |
|
46 | 45 | public class JDKFileTreeWatch extends JDKBaseWatch { |
47 | 46 | private final Logger logger = LogManager.getLogger(); |
| 47 | + private final Path rootPath; |
| 48 | + private final Path relativePathParent; |
48 | 49 | private final Map<Path, JDKFileTreeWatch> childWatches = new ConcurrentHashMap<>(); |
49 | | - private final @NotOnlyInitialized JDKBaseWatch internal; |
| 50 | + private final JDKBaseWatch internal; |
50 | 51 |
|
51 | | - public JDKFileTreeWatch(Path root, Executor exec, |
| 52 | + public JDKFileTreeWatch(Path fullPath, Executor exec, |
| 53 | + BiConsumer<EventHandlingWatch, WatchEvent> eventHandler) { |
| 54 | + this(fullPath, Path.of(""), exec, eventHandler); |
| 55 | + } |
| 56 | + |
| 57 | + public JDKFileTreeWatch(Path rootPath, Path relativePathParent, Executor exec, |
52 | 58 | BiConsumer<EventHandlingWatch, WatchEvent> eventHandler) { |
53 | 59 |
|
54 | | - super(root, exec, eventHandler); |
| 60 | + super(rootPath.resolve(relativePathParent), exec, eventHandler); |
| 61 | + this.rootPath = rootPath; |
| 62 | + this.relativePathParent = relativePathParent; |
| 63 | + |
55 | 64 | var internalEventHandler = eventHandler.andThen(new ChildWatchesUpdater()); |
56 | | - this.internal = new JDKDirectoryWatch(root, exec, internalEventHandler); |
| 65 | + this.internal = new JDKDirectoryWatch(path, exec, internalEventHandler) { |
| 66 | + |
| 67 | + // Override to ensure that this watch relativizes events wrt |
| 68 | + // `rootPath` (instead of `path`, as is the default behavior) |
| 69 | + @Override |
| 70 | + public WatchEvent relativize(WatchEvent event) { |
| 71 | + return new WatchEvent(event.getKind(), rootPath, |
| 72 | + rootPath.relativize(event.calculateFullPath())); |
| 73 | + } |
| 74 | + |
| 75 | + // Override to ensure that this watch translates JDK events using |
| 76 | + // `rootPath` (instead of `path`, as is the default behavior). |
| 77 | + // Events returned by this method do not need to be relativized. |
| 78 | + @Override |
| 79 | + protected WatchEvent translate(java.nio.file.WatchEvent<?> jdkEvent) { |
| 80 | + var kind = translate(jdkEvent.kind()); |
| 81 | + |
| 82 | + Path relativePath = null; |
| 83 | + if (kind != WatchEvent.Kind.OVERFLOW) { |
| 84 | + var child = (Path) jdkEvent.context(); |
| 85 | + if (child != null) { |
| 86 | + relativePath = relativePathParent.resolve(child); |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + var event = new WatchEvent(kind, rootPath, relativePath); |
| 91 | + logger.trace("Translated: {} to {}", jdkEvent, event); |
| 92 | + return event; |
| 93 | + } |
| 94 | + }; |
57 | 95 | } |
58 | 96 |
|
59 | 97 | /** |
@@ -98,27 +136,20 @@ private void acceptDeleted(Path fullPath) { |
98 | 136 | } |
99 | 137 | } |
100 | 138 |
|
101 | | - private void reportOverflowTo(EventHandlingWatch childWatch) { |
102 | | - var overflow = new WatchEvent(WatchEvent.Kind.OVERFLOW, childWatch.getPath()); |
| 139 | + private void reportOverflowTo(JDKFileTreeWatch childWatch) { |
| 140 | + var overflow = new WatchEvent(WatchEvent.Kind.OVERFLOW, |
| 141 | + childWatch.rootPath, childWatch.relativePathParent); |
103 | 142 | childWatch.handleEvent(overflow); |
104 | 143 | } |
105 | 144 | } |
106 | 145 |
|
107 | 146 | private JDKFileTreeWatch openChildWatch(Path child) { |
108 | | - Function<Path, JDKFileTreeWatch> newChildWatch = p -> new JDKFileTreeWatch(child, exec, (w, e) -> |
109 | | - // Same as `eventHandler`, except each event is pre-processed such |
110 | | - // that the last segment of the root path becomes the first segment |
111 | | - // of the relative path. For instance, `foo/bar` (root path) and |
112 | | - // `baz.txt` (relative path) are pre-processed to `foo` (root path) |
113 | | - // and `bar/baz.txt` (relative path). This is to ensure the parent |
114 | | - // directory of a child directory is reported as the root directory |
115 | | - // of the event. |
116 | | - eventHandler.accept(w, relativize(e)) |
117 | | - ); |
| 147 | + Function<Path, JDKFileTreeWatch> newChildWatch = p -> new JDKFileTreeWatch( |
| 148 | + rootPath, rootPath.relativize(child), exec, eventHandler); |
118 | 149 |
|
119 | 150 | var childWatch = childWatches.computeIfAbsent(child, newChildWatch); |
120 | 151 | try { |
121 | | - childWatch.open(); |
| 152 | + childWatch.startIfFirstTime(); |
122 | 153 | } catch (IOException e) { |
123 | 154 | logger.error("Could not open (nested) file tree watch for: {} ({})", child, e); |
124 | 155 | } |
|
0 commit comments