@NotNull @SuppressWarnings("Duplicates") private Collection<String> checkWatchable( String reportedPath, boolean isExact, boolean fastPath) { if (reportedPath == null) return Collections.emptyList(); List<String> flatWatchRoots = myFlatWatchRoots; List<String> recursiveWatchRoots = myRecursiveWatchRoots; if (flatWatchRoots.isEmpty() && recursiveWatchRoots.isEmpty()) return Collections.emptyList(); List<Pair<String, String>> mapping = myMapping; Collection<String> affectedPaths = new SmartList<String>(reportedPath); for (Pair<String, String> map : mapping) { if (FileUtil.startsWith(reportedPath, map.first)) { affectedPaths.add(map.second + reportedPath.substring(map.first.length())); } else if (FileUtil.startsWith(reportedPath, map.second)) { affectedPaths.add(map.first + reportedPath.substring(map.second.length())); } } Collection<String> changedPaths = new SmartList<String>(); ext: for (String path : affectedPaths) { if (fastPath && !changedPaths.isEmpty()) break; for (String root : flatWatchRoots) { if (FileUtil.namesEqual(path, root)) { changedPaths.add(path); continue ext; } if (isExact) { String parentPath = new File(path).getParent(); if (parentPath != null && FileUtil.namesEqual(parentPath, root)) { changedPaths.add(path); continue ext; } } } for (String root : recursiveWatchRoots) { if (FileUtil.startsWith(path, root)) { changedPaths.add(path); continue ext; } if (!isExact) { String parentPath = new File(root).getParent(); if (parentPath != null && FileUtil.namesEqual(path, parentPath)) { changedPaths.add(root); continue ext; } } } } return changedPaths; }
private void processChange(String path, WatcherOp op) { if (SystemInfo.isWindows && op == WatcherOp.RECDIRTY && path.length() == 3 && Character.isLetter(path.charAt(0))) { VirtualFile root = LocalFileSystem.getInstance().findFileByPath(path); if (root != null) { myNotificationSink.notifyPathsRecursive(list(root.getPresentableUrl())); } notifyOnAnyEvent(); return; } if (op == WatcherOp.CHANGE) { // collapse subsequent change file change notifications that happen once we copy large file, // this allows reduction of path checks at least 20% for Windows synchronized (myLock) { for (int i = 0; i < myLastChangedPaths.length; ++i) { int last = myLastChangedPathIndex - i - 1; if (last < 0) last += myLastChangedPaths.length; String lastChangedPath = myLastChangedPaths[last]; if (lastChangedPath != null && lastChangedPath.equals(path)) { return; } } myLastChangedPaths[myLastChangedPathIndex++] = path; if (myLastChangedPathIndex == myLastChangedPaths.length) myLastChangedPathIndex = 0; } } int length = path.length(); if (length > 1 && path.charAt(length - 1) == '/') path = path.substring(0, length - 1); boolean exactPath = op != WatcherOp.DIRTY && op != WatcherOp.RECDIRTY; Collection<String> paths = checkWatchable(path, exactPath, false); if (paths.isEmpty()) { if (LOG.isDebugEnabled()) { LOG.debug("Not watchable, filtered: " + path); } return; } switch (op) { case STATS: case CHANGE: myNotificationSink.notifyDirtyPaths(paths); break; case CREATE: case DELETE: myNotificationSink.notifyPathsCreatedOrDeleted(paths); break; case DIRTY: myNotificationSink.notifyDirtyDirectories(paths); break; case RECDIRTY: myNotificationSink.notifyPathsRecursive(paths); break; default: LOG.error("Unexpected op: " + op); } notifyOnAnyEvent(); }