diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java index 5a25c95d05c4..063cb2f2bd8b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java @@ -30,17 +30,15 @@ import com.intellij.openapi.vfs.newvfs.events.*; import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile; import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.OpenTHashSet; import com.intellij.util.containers.Queue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; import static com.intellij.openapi.diagnostic.LogUtil.debug; -import static com.intellij.util.containers.ContainerUtil.newHashSet; +import static com.intellij.util.containers.ContainerUtil.newTroveSet; /** * @author max @@ -122,10 +120,14 @@ public class RefreshWorker { if (fullSync) { String[] currentNames = persistence.list(file); String[] upToDateNames = VfsUtil.filterNames(fs.list(file)); - Set newNames = newHashSet(upToDateNames); + Set newNames = newTroveSet(FileUtil.PATH_HASHING_STRATEGY, upToDateNames); ContainerUtil.removeAll(newNames, currentNames); - Set deletedNames = newHashSet(currentNames); + Set deletedNames = newTroveSet(FileUtil.PATH_HASHING_STRATEGY, currentNames); ContainerUtil.removeAll(deletedNames, upToDateNames); + OpenTHashSet actualNames = null; + if (!SystemInfo.isFileSystemCaseSensitive) { + actualNames = new OpenTHashSet(FileUtil.PATH_HASHING_STRATEGY, upToDateNames); + } debug(LOG, "current=%s +%s -%s", currentNames, newNames, deletedNames); for (String name : deletedNames) { @@ -149,6 +151,7 @@ public class RefreshWorker { FileAttributes childAttributes = fs.getAttributes(child); if (childAttributes != null) { checkAndScheduleChildRefresh(file, child, childAttributes); + checkAndScheduleFileNameChange(actualNames, child); } else { LOG.warn("fs=" + fs + " dir=" + file + " name=" + child.getName()); @@ -159,12 +162,18 @@ public class RefreshWorker { } else { Collection cachedChildren = file.getCachedChildren(); - debug(LOG, "cached=%s", cachedChildren); + OpenTHashSet actualNames = null; + if (!SystemInfo.isFileSystemCaseSensitive) { + actualNames = new OpenTHashSet(FileUtil.PATH_HASHING_STRATEGY, VfsUtil.filterNames(fs.list(file))); + } + debug(LOG, "cached=%s actual=%s", cachedChildren, actualNames); + for (VirtualFile child : cachedChildren) { checkCancelled(); FileAttributes childAttributes = fs.getAttributes(child); if (childAttributes != null) { checkAndScheduleChildRefresh(file, child, childAttributes); + checkAndScheduleFileNameChange(actualNames, child); } else { scheduleDeletion(child); @@ -223,6 +232,16 @@ public class RefreshWorker { } } + private void checkAndScheduleFileNameChange(@Nullable OpenTHashSet actualNames, VirtualFile child) { + if (actualNames != null) { + String currentName = child.getName(); + String actualName = actualNames.get(currentName); + if (!currentName.equals(actualName)) { + scheduleAttributeChange(child, VirtualFile.PROP_NAME, currentName, actualName); + } + } + } + private static class RefreshCancelledException extends RuntimeException { } private void checkCancelled() { 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 e37fdba05731..4dc17f00dfad 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 @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Ref; 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.*; import com.intellij.openapi.vfs.impl.local.FileWatcher; import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl; @@ -651,6 +652,28 @@ public class FileWatcherTest extends PlatformLangTestCase { } } + public void testFileCaseChange() throws Exception { + if (SystemInfo.isFileSystemCaseSensitive) { + System.err.println("Ignored: case-insensitive FS required"); + return; + } + + File topDir = createTestDir("topDir"); + File testFile = createTestFile(topDir, "file.txt", "123"); + refresh(topDir); + + LocalFileSystem.WatchRequest request = watch(topDir); + try { + myAccept = true; + File newFile = new File(testFile.getParent(), StringUtil.capitalize(testFile.getName())); + FileUtil.rename(testFile, newFile); + assertEvent(VFilePropertyChangeEvent.class, newFile.getPath()); + } + finally { + unwatch(request); + } + } + @NotNull private LocalFileSystem.WatchRequest watch(File watchFile) { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java index 4592a813ea7d..c3a86415f80e 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java @@ -20,6 +20,7 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.IoTestUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.newvfs.ManagingFS; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; @@ -402,4 +403,35 @@ public class LocalFileSystemTest extends PlatformLangTestCase { assertTrue(topDir.exists()); assertEquals(2, topDir.getChildren().length); } + + public void testFileCaseChange() throws Exception { + if (SystemInfo.isFileSystemCaseSensitive) { + System.err.println("Ignored: case-insensitive FS required"); + return; + } + + File top = createTempDirectory(false); + File file = IoTestUtil.createTestFile(top, "file.txt", "test"); + + LocalFileSystem lfs = LocalFileSystem.getInstance(); + VirtualFile topDir = lfs.refreshAndFindFileByIoFile(top); + assertNotNull(topDir); + VirtualFile sourceFile = lfs.refreshAndFindFileByIoFile(file); + assertNotNull(sourceFile); + + String newName = StringUtil.capitalize(file.getName()); + FileUtil.rename(file, new File(top, newName)); + topDir.refresh(false, true); + assertFalse(((VirtualDirectoryImpl)topDir).allChildrenLoaded()); + assertTrue(sourceFile.isValid()); + assertEquals(newName, sourceFile.getName()); + + topDir.getChildren(); + newName = newName.toLowerCase(); + FileUtil.rename(file, new File(top, newName)); + topDir.refresh(false, true); + assertTrue(((VirtualDirectoryImpl)topDir).allChildrenLoaded()); + assertTrue(sourceFile.isValid()); + assertEquals(newName, sourceFile.getName()); + } } diff --git a/platform/util/src/com/intellij/util/containers/OpenTHashSet.java b/platform/util/src/com/intellij/util/containers/OpenTHashSet.java index f2aa453a1439..5834cb935fb4 100644 --- a/platform/util/src/com/intellij/util/containers/OpenTHashSet.java +++ b/platform/util/src/com/intellij/util/containers/OpenTHashSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import gnu.trove.THashSet; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.Collection; /** @@ -60,13 +61,18 @@ public class OpenTHashSet extends THashSet { super(ts, strategy); } + public OpenTHashSet(final TObjectHashingStrategy strategy, final T... ts) { + super(Arrays.asList(ts), strategy); + } + @Override public int index(final T obj) { return super.index(obj); } public T get(final int index) { - return (T)_set[index]; + @SuppressWarnings("unchecked") T t = (T)_set[index]; + return t; } /** @@ -77,5 +83,4 @@ public class OpenTHashSet extends THashSet { final int index = index(obj); return index < 0 ? null : get(index); } - }