From fd91d88e5e451b78bbf26e6100a860ae80a18c89 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 28 Nov 2012 14:27:11 +0100 Subject: [PATCH] IDEA-94515 (correct Unix paths splitting) --- .../vfs/impl/local/LocalFileSystemImpl.java | 67 +++++++++++-------- .../openapi/vfs/local/FileWatcherTest.java | 60 ++++++++++++++--- .../util/containers/ContainerUtil.java | 5 ++ 3 files changed, 97 insertions(+), 35 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java index c4ddb41a944a..f4630d5b8481 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java @@ -20,8 +20,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -32,6 +32,7 @@ import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; import com.intellij.util.Consumer; import com.intellij.util.TimeoutUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -44,6 +45,8 @@ import java.io.IOException; import java.util.*; public final class LocalFileSystemImpl extends LocalFileSystemBase implements ApplicationComponent { + private static final String FS_ROOT = "/"; + private final Object myLock = new Object(); private final List myRootsToWatch = new ArrayList(); private TreeNode myNormalizedTree = null; @@ -107,7 +110,7 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap private static class TreeNode { private WatchRequestImpl watchRequest = null; - private Map nodes = new HashMap(); + private Map nodes = ContainerUtil.newTroveMap(FileUtil.PATH_HASHING_STRATEGY); } public LocalFileSystemImpl(@NotNull ManagingFS managingFS) { @@ -164,13 +167,12 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap synchronized (myLock) { TreeNode rootNode = new TreeNode(); for (WatchRequestImpl request : myRootsToWatch) { + request.myDominated = false; String rootPath = request.getRootPath(); + TreeNode currentNode = rootNode; MainLoop: - for (String subPath : rootPath.split("/")) { - if (!SystemInfo.isFileSystemCaseSensitive) { - subPath = subPath.toLowerCase(); - } + for (String subPath : splitPath(rootPath)) { TreeNode nextNode = currentNode.nodes.get(subPath); if (nextNode != null) { currentNode = nextNode; @@ -228,6 +230,23 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap return result.toArray(new WatchRequestImpl[result.size()]); } + @NotNull + private static List splitPath(@NotNull String path) { + if (path.isEmpty()) { + return ContainerUtil.emptyList(); + } + + if (FS_ROOT.equals(path)) { + return Collections.singletonList(FS_ROOT); + } + + List parts = StringUtil.split(path, FS_ROOT); + if (StringUtil.startsWithChar(path, '/')) { + parts.add(0, FS_ROOT); + } + return parts; + } + private static void visitTree(TreeNode rootNode, Consumer consumer) { for (TreeNode node : rootNode.nodes.values()) { consumer.consume(node); @@ -242,10 +261,7 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap String rootPath = request.getRootPath(); TreeNode currentNode = myNormalizedTree; - for (String subPath : rootPath.split("/")) { - if (!SystemInfo.isFileSystemCaseSensitive) { - subPath = subPath.toLowerCase(); - } + for (String subPath : splitPath(rootPath)) { TreeNode nextNode = currentNode.nodes.get(subPath); if (nextNode == null) { return false; @@ -332,27 +348,24 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap private void setUpFileWatcher() { final Application application = ApplicationManager.getApplication(); if (application.isDisposeInProgress() || !myWatcher.isOperational()) return; + application.assertReadAccessAllowed(); - application.runReadAction(new Runnable() { - public void run() { - synchronized (myLock) { - final WatchRequestImpl[] watchRequests = normalizeRootsForRefresh(); - final List myRecursiveRoots = new ArrayList(); - final List myFlatRoots = new ArrayList(); + synchronized (myLock) { + final WatchRequestImpl[] watchRequests = normalizeRootsForRefresh(); + final List myRecursiveRoots = new ArrayList(); + final List myFlatRoots = new ArrayList(); - for (WatchRequestImpl watchRequest : watchRequests) { - if (watchRequest.isToWatchRecursively()) { - myRecursiveRoots.add(watchRequest.myFSRootPath); - } - else { - myFlatRoots.add(watchRequest.myFSRootPath); - } - } - - myWatcher.setWatchRoots(myRecursiveRoots, myFlatRoots); + for (WatchRequestImpl watchRequest : watchRequests) { + if (watchRequest.isToWatchRecursively()) { + myRecursiveRoots.add(watchRequest.myFSRootPath); + } + else { + myFlatRoots.add(watchRequest.myFSRootPath); } } - }); + + myWatcher.setWatchRoots(myRecursiveRoots, myFlatRoots); + } } private class StoreRefreshStatusThread extends Thread { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java index fd0a2b176ab6..a91c107b259c 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java @@ -94,6 +94,7 @@ public class FileWatcherTest extends PlatformLangTestCase { assertTrue(myWatcher.isOperational()); myAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, getProject()); + myTimeout = NATIVE_PROCESS_DELAY; myConnection = ApplicationManager.getApplication().getMessageBus().connect(); myConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { @@ -225,15 +226,11 @@ public class FileWatcherTest extends PlatformLangTestCase { FileUtil.writeToFile(watchedFile, "new content"); assertEvent(VFileContentChangeEvent.class, watchedFile.getAbsolutePath()); + myTimeout = 10 * INTER_RESPONSE_DELAY; myAccept = true; - try { - myTimeout = 10 * INTER_RESPONSE_DELAY; - FileUtil.writeToFile(unwatchedFile, "new content"); - assertEvent(VFileEvent.class); - } - finally { - myTimeout = NATIVE_PROCESS_DELAY; - } + FileUtil.writeToFile(unwatchedFile, "new content"); + assertEvent(VFileEvent.class); + myTimeout = NATIVE_PROCESS_DELAY; } finally { unwatch(request); @@ -506,6 +503,53 @@ public class FileWatcherTest extends PlatformLangTestCase { } } + public void testSwitchingToFsRoot() throws Exception { + File topDir = IoTestUtil.createTestDir("top"); + File rootDir = IoTestUtil.createTestDir(topDir, "root"); + File file1 = IoTestUtil.createTestFile(topDir, "1.txt"); + File file2 = IoTestUtil.createTestFile(rootDir, "2.txt"); + refresh(topDir); + + File fsRoot = new File(SystemInfo.isUnix ? "/" : topDir.getPath().substring(0, topDir.getPath().indexOf(File.separatorChar))); + assertTrue("can't guess root of " + topDir, fsRoot.exists()); + + LocalFileSystem.WatchRequest request = watch(rootDir); + try { + myAccept = true; + FileUtil.writeToFile(file1, "abc"); + FileUtil.writeToFile(file2, "abc"); + assertEvent(VFileContentChangeEvent.class, file2.getPath()); + + LocalFileSystem.WatchRequest rootRequest = watch(fsRoot); + try { + myTimeout = 10 * INTER_RESPONSE_DELAY; + myAccept = true; + FileUtil.writeToFile(file1, "12345"); + FileUtil.writeToFile(file2, "12345"); + assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath()); + myTimeout = NATIVE_PROCESS_DELAY; + } + finally { + unwatch(rootRequest); + } + + myAccept = true; + FileUtil.writeToFile(file1, ""); + FileUtil.writeToFile(file2, ""); + assertEvent(VFileContentChangeEvent.class, file2.getPath()); + } + finally { + unwatch(request); + } + + myTimeout = 10 * INTER_RESPONSE_DELAY; + myAccept = true; + FileUtil.writeToFile(file1, "xyz"); + FileUtil.writeToFile(file2, "xyz"); + assertEvent(VFileEvent.class); + myTimeout = NATIVE_PROCESS_DELAY; + } + @NotNull private LocalFileSystem.WatchRequest watch(final File watchFile) { diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 030124122e9f..c5172744c74c 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -86,6 +86,11 @@ public class ContainerUtil extends ContainerUtilRt { return new THashMap(); } + @NotNull + public static THashMap newTroveMap(@NotNull TObjectHashingStrategy strategy) { + return new THashMap(strategy); + } + @NotNull public static IdentityHashMap newIdentityHashMap() { return new IdentityHashMap();