From bb6ae1d5c14a0b85e67259e6280ddf6211cb7905 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 30 Jul 2012 20:30:11 +0200 Subject: [PATCH] Use bulk file attributes reading --- .../openapi/vfs/impl/jar/JarHandlerBase.java | 13 +- .../vfs/newvfs/FileSystemInterface.java | 8 +- .../vfs/newvfs/NewVirtualFileSystem.java | 37 ---- .../openapi/vfs/ex/temp/TempFileSystem.java | 10 - .../vfs/impl/jar/JarFileSystemImpl.java | 21 +- .../vfs/impl/local/LocalFileSystemBase.java | 81 +++----- .../openapi/vfs/impl/win32/Win32FsCache.java | 23 --- .../vfs/impl/win32/Win32LocalFileSystem.java | 23 ++- .../vfs/newvfs/impl/VirtualDirectoryImpl.java | 10 +- .../newvfs/impl/VirtualFileSystemEntry.java | 6 +- .../vfs/newvfs/persistent/PersistentFS.java | 82 ++++---- .../vfs/newvfs/persistent/RefreshWorker.java | 188 +++++++++--------- .../vfs/local/LocalFileSystemTest.java | 23 ++- .../vfs/local/SymlinkHandlingTest.java | 25 ++- .../intellij/mock/MockLocalFileSystem.java | 5 - .../openapi/util/io/FileAttributes.java | 12 ++ .../openapi/util/io/FileSystemUtil.java | 3 +- .../intellij/openapi/util/io/FileUtil.java | 100 +++------- 18 files changed, 283 insertions(+), 387 deletions(-) diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java index c75fa6105263..61422556810a 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java @@ -38,6 +38,9 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class JarHandlerBase { + private static final long DEFAULT_LENGTH = 0L; + private static final long DEFAULT_TIMESTAMP = -1L; + protected final TimedReference myJarFile = new TimedReference(null); protected SoftReference> myRelPathsToEntries = new SoftReference>(null); protected final Object lock = new Object(); @@ -246,7 +249,7 @@ public class JarHandlerBase { public long getLength(@NotNull final VirtualFile file) { synchronized (lock) { final JarFile.JarEntry entry = convertToEntry(file); - return entry != null ? entry.getSize() : 0; + return entry != null ? entry.getSize() : DEFAULT_LENGTH; } } @@ -282,7 +285,7 @@ public class JarHandlerBase { if (file.getParent() == null) return getOriginalFile().lastModified(); // Optimization synchronized (lock) { final JarFile.JarEntry entry = convertToEntry(file); - return entry != null ? entry.getTime() : -1L; + return entry != null ? entry.getTime() : DEFAULT_TIMESTAMP; } } @@ -307,8 +310,12 @@ public class JarHandlerBase { @Nullable public FileAttributes getAttributes(@NotNull final VirtualFile file) { synchronized (lock) { + final EntryInfo entryInfo = getEntryInfo(getRelativePath(file)); + if (entryInfo == null) return null; final JarFile.JarEntry entry = convertToEntry(file); - return entry != null ? new FileAttributes(entry.isDirectory(), false, false, false, entry.getSize(), entry.getTime(), false) : null; + final long length = entry != null ? entry.getSize() : DEFAULT_LENGTH; + final long timeStamp = entry != null ? entry.getTime() : DEFAULT_TIMESTAMP; + return new FileAttributes(entryInfo.isDirectory, false, false, false, length, timeStamp, false); } } } diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/FileSystemInterface.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/FileSystemInterface.java index bb16fae1ee50..854cc5239bb8 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/FileSystemInterface.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/FileSystemInterface.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -28,7 +28,11 @@ import java.io.InputStream; import java.io.OutputStream; public interface FileSystemInterface { - boolean exists(@NotNull VirtualFile fileOrDirectory); + // default values for missing files (same as in corresponding java.io.File methods) + long DEFAULT_LENGTH = 0; + long DEFAULT_TIMESTAMP = 0; + + boolean exists(@NotNull VirtualFile file); @NotNull String[] list(@NotNull VirtualFile file); diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFileSystem.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFileSystem.java index a132ea30cdab..3beff3e91e7d 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFileSystem.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFileSystem.java @@ -17,7 +17,6 @@ package com.intellij.openapi.vfs.newvfs; import com.intellij.openapi.util.io.FileAttributes; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; @@ -116,42 +115,6 @@ public abstract class NewVirtualFileSystem extends VirtualFileSystem implements return file.getName(); } - /** - * Queries the file about several attributes at once, and returns them ORed together. - * This method is typically faster than several methods calls querying corresponding file attributes one by one. - * - * @param file to query. - * @param flags Attributes to query the file for. - * Each attribute is an int constant from this class. - * Following attributes are defined: - *
    - *
  • {@link com.intellij.openapi.util.io.FileUtil#BA_EXISTS} is set if {@link java.io.File#exists()} returns true
  • - *
  • {@link com.intellij.openapi.util.io.FileUtil#BA_DIRECTORY} is set if {@link java.io.File#isDirectory()} returns true
  • - *
  • {@link com.intellij.openapi.util.io.FileUtil#BA_HIDDEN} is set if {@link java.io.File#isHidden()} returns true
  • - *
  • {@link com.intellij.openapi.util.io.FileUtil#BA_REGULAR} is set if {@link java.io.File#isFile()} returns true
  • - *
- * Attributes can be bitwise ORed together to query several file attributes at once. - * -1 as an argument value will query all attributes. - * @return Attributes mask for the file, where the bit is set if the corresponding attribute for the file is true. - * That is, the return value is
{@code
-   *           (file.exists() ? BA_EXISTS : 0) |
-   *           (file.isDirectory()() ? BA_DIRECTORY : 0) |
-   *           (file.isRegular()() ? BA_REGULAR : 0) |
-   *           (file.isHidden()() ? BA_HIDDEN : 0)
-   *           }
- * Except that the bit in the return value is undefined if the corresponding bit in the flags parameter is not set. - *

- * Example usage: - *

{@code
-   *  int attributes = getBooleanAttributes(file, BA_EXISTS | BA_DIRECTORY);
-   *  if ((attributes & BA_EXISTS) != 0) {
-   *    // file exists
-   *    boolean isDirectory = (attributes & BA_DIRECTORY) != 0;
-   *  }}
- */ - @FileUtil.FileBooleanAttributes - public abstract int getBooleanAttributes(@NotNull final VirtualFile file, @FileUtil.FileBooleanAttributes int flags); - /** * Reads various file attributes in one shot (to reduce the number of native I/O calls). * diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java index 7e81c94562c0..a8e93ae8bf1d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java @@ -19,7 +19,6 @@ package com.intellij.openapi.vfs.ex.temp; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream; import com.intellij.openapi.util.io.FileAttributes; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.ManagingFS; @@ -368,15 +367,6 @@ public class TempFileSystem extends NewVirtualFileSystem { } } - @Override - public int getBooleanAttributes(@NotNull VirtualFile file, int flags) { - FSItem item = convert(file); - int isDir = item instanceof FSDir ? FileUtil.BA_DIRECTORY : 0; - int exists = item == null ? 0 : FileUtil.BA_EXISTS; - int regular = isDir == 0 ? FileUtil.BA_REGULAR : 0; - return isDir | exists | regular; - } - @Override public FileAttributes getAttributes(@NotNull final VirtualFile file) { final FSItem item = convert(file); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarFileSystemImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarFileSystemImpl.java index 743dbcb93d69..c3d64b4cb932 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarFileSystemImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarFileSystemImpl.java @@ -359,24 +359,6 @@ public class JarFileSystemImpl extends JarFileSystem implements ApplicationCompo return VfsImplUtil.refreshAndFindFileByPath(this, path); } - @Override - public int getBooleanAttributes(@NotNull VirtualFile file, int flags) { - int exists = 0; - JarHandler handler = getHandler(file); - if ((flags & FileUtil.BA_EXISTS) != 0) { - exists = handler.exists(file) ? FileUtil.BA_EXISTS : 0; - } - int isDir = 0; - if ((flags & FileUtil.BA_DIRECTORY) != 0) { - isDir = handler.isDirectory(file) ? FileUtil.BA_DIRECTORY : 0; - } - int regular = 0; - if ((flags & FileUtil.BA_REGULAR) != 0) { - regular = isDir == 0 ? FileUtil.BA_REGULAR : 0; - } - return exists | isDir | regular; - } - @Override public FileAttributes getAttributes(@NotNull final VirtualFile file) { final JarHandler handler = getHandler(file); @@ -385,8 +367,7 @@ public class JarFileSystemImpl extends JarFileSystem implements ApplicationCompo if (file.getParent() == null) { final LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); final VirtualFile originalFile = localFileSystem.findFileByIoFile(handler.getOriginalFile()); - assert originalFile != null : file; - return localFileSystem.getAttributes(originalFile); + return originalFile != null ? localFileSystem.getAttributes(originalFile) : null; } return handler.getAttributes(file); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java index f5bdf93eaff7..36ce89ac7034 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java @@ -52,8 +52,8 @@ import java.util.Locale; public abstract class LocalFileSystemBase extends LocalFileSystem { protected static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl"); - protected static final long DEFAULT_LENGTH = 0; - protected static final long DEFAULT_TIMESTAMP = 0; + private static final FileAttributes FAKE_ROOT_ATTRIBUTES = + new FileAttributes(true, false, false, false, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, false); private final List myHandlers = new ArrayList(); @@ -143,14 +143,8 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { private static File convertToIOFileAndCheck(@NotNull final VirtualFile file) throws FileNotFoundException { final File ioFile = convertToIOFile(file); - int intFlags = FileUtil.getBooleanAttributes(ioFile); - if (intFlags != -1) { - if ((intFlags & FileUtil.BA_EXISTS) != 0 && (intFlags & FileUtil.BA_REGULAR) == 0) { - throw new FileNotFoundException("Not a file: " + ioFile); - } - return ioFile; - } - if (ioFile.exists() && !ioFile.isFile()) { + final FileAttributes attributes = FileSystemUtil.getAttributes(ioFile); + if (attributes != null && !attributes.isFile()) { throw new FileNotFoundException("Not a file: " + ioFile); } @@ -158,36 +152,38 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { } @Override - public boolean exists(@NotNull final VirtualFile fileOrDirectory) { - String path = fileOrDirectory.getPath(); - if (fileOrDirectory.getParent() == null && path.startsWith("//")) return true; // Windows UNC root like //unit-333 - if (StringUtil.isEmpty(path)) return true; // fake top dir for Windows - return convertToIOFile(fileOrDirectory).exists(); + public boolean exists(@NotNull final VirtualFile file) { + return getAttributes(file) != null; } @Override public long getLength(@NotNull final VirtualFile file) { - return convertToIOFile(file).length(); + final FileAttributes attributes = getAttributes(file); + return attributes != null ? attributes.length : DEFAULT_LENGTH; } @Override public long getTimeStamp(@NotNull final VirtualFile file) { - return convertToIOFile(file).lastModified(); + final FileAttributes attributes = getAttributes(file); + return attributes != null ? attributes.lastModified : DEFAULT_TIMESTAMP; } @Override public boolean isDirectory(@NotNull final VirtualFile file) { - return convertToIOFile(file).isDirectory(); + final FileAttributes attributes = getAttributes(file); + return attributes != null && attributes.isDirectory(); } @Override public boolean isWritable(@NotNull final VirtualFile file) { - return convertToIOFile(file).canWrite(); + final FileAttributes attributes = getAttributes(file); + return attributes != null && attributes.isWritable(); } @Override public boolean isSymLink(@NotNull final VirtualFile file) { - return FileSystemUtil.isSymLink(file.getPath()); + final FileAttributes attributes = getAttributes(file); + return attributes != null && attributes.isSymLink(); } @Override @@ -197,17 +193,8 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { @Override public boolean isSpecialFile(@NotNull final VirtualFile file) { - if (!SystemInfo.isUnix) return false; - final File ioFile = convertToIOFile(file); - return isSpecialFile(ioFile); - } - - private static boolean isSpecialFile(@NotNull File ioFile) { - int flags = FileUtil.getBooleanAttributes(ioFile); - if (flags != -1) { - return (flags & (FileUtil.BA_REGULAR | FileUtil.BA_DIRECTORY | FileUtil.BA_EXISTS)) == (~FileUtil.BA_REGULAR & ~FileUtil.BA_DIRECTORY & FileUtil.BA_EXISTS); - } - return !ioFile.isFile() && !ioFile.isDirectory() && ioFile.exists(); + final FileAttributes attributes = getAttributes(file); + return attributes != null && attributes.isSpecial(); } @Override @@ -243,6 +230,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { return names == null ? ArrayUtil.EMPTY_STRING_ARRAY : names; } + // todo[r.sh] drop the restriction in favor of VfsUtilCore.visitChildrenRecursively() protected static boolean isInvalidSymLink(@NotNull final VirtualFile file) { if (!file.isSymLink()) return false; final VirtualFile target = file.getCanonicalFile(); @@ -301,7 +289,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { } } - RefreshQueue.getInstance().refresh(async, recursive, onFinish, VfsUtil.toVirtualFileArray(filesToRefresh)); + RefreshQueue.getInstance().refresh(async, recursive, onFinish, VfsUtilCore.toVirtualFileArray(filesToRefresh)); } finally { if (fireCommonRefreshSession) manager.fireAfterRefreshFinish(false); @@ -323,7 +311,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { collection = new ArrayList(); ContainerUtil.addAll(collection, files); } - RefreshQueue.getInstance().refresh(async, recursive, onFinish, VfsUtil.toVirtualFileArray(collection)); + RefreshQueue.getInstance().refresh(async, recursive, onFinish, VfsUtilCore.toVirtualFileArray(collection)); } @Override @@ -570,11 +558,13 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { @NotNull final VirtualFile vFile, @NotNull final VirtualFile newParent, @NotNull final String copyName) throws IOException { - File physicalFile = convertToIOFile(vFile); - if (isSpecialFile(physicalFile)) { - throw new FileNotFoundException("Not a file: " + physicalFile); + final FileAttributes attributes = getAttributes(vFile); + + if (attributes == null || attributes.isSpecial()) { + throw new FileNotFoundException("Not a file: " + vFile); } + File physicalFile = convertToIOFile(vFile); File physicalCopy = auxCopy(vFile, newParent, copyName); try { @@ -583,7 +573,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { physicalCopy = new File(newPhysicalParent, copyName); try { - if (physicalFile.isDirectory()) { + if (attributes.isDirectory()) { FileUtil.copyDir(physicalFile, physicalCopy); } else { @@ -604,6 +594,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { } }); } + return new FakeVirtualFile(newParent, copyName); } @@ -725,19 +716,13 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { } } - @Override - public int getBooleanAttributes(@NotNull VirtualFile file, int flags) { - int attributes = FileUtil.getBooleanAttributes(convertToIOFile(file)); - if (attributes != -1) return attributes & flags; - return ((flags & FileUtil.BA_EXISTS) != 0 && exists(file) ? FileUtil.BA_EXISTS : 0) | - ((flags & FileUtil.BA_DIRECTORY) != 0 && isDirectory(file) ? FileUtil.BA_DIRECTORY : 0) | - ((flags & FileUtil.BA_REGULAR) != 0 && !isSpecialFile(file) ? FileUtil.BA_REGULAR : 0) | - ((flags & FileUtil.BA_HIDDEN) != 0 && convertToIOFile(file).isHidden() ? FileUtil.BA_HIDDEN : 0); - } - @Override public FileAttributes getAttributes(@NotNull final VirtualFile file) { - return FileSystemUtil.getAttributes(FileUtil.toSystemDependentName(file.getPath())); + final String path = file.getPath(); + if (StringUtil.isEmpty(path) || file.getParent() == null && path.startsWith("//")) { + return FAKE_ROOT_ATTRIBUTES; // fake Windows roots + } + return FileSystemUtil.getAttributes(FileUtil.toSystemDependentName(path)); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32FsCache.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32FsCache.java index e5e16133a219..897126abb4a1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32FsCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32FsCache.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.vfs.impl.win32; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.win32.FileInfo; import com.intellij.openapi.util.io.win32.IdeaWin32; import com.intellij.openapi.vfs.VirtualFile; @@ -81,26 +80,4 @@ class Win32FsCache { } return info; } - - @FileUtil.FileBooleanAttributes - int getBooleanAttributes(@NotNull VirtualFile file, @FileUtil.FileBooleanAttributes int flags) { - FileInfo info = getInfo(file); - if (info == null) return 0; - - int result = 0; - if ((flags & FileUtil.BA_EXISTS) != 0) { - result |= FileUtil.BA_EXISTS; - } - if ((flags & FileUtil.BA_DIRECTORY) != 0) { - result |= (info.attributes & FileInfo.FILE_ATTRIBUTE_DIRECTORY) == 0 ? 0 : FileUtil.BA_DIRECTORY; - } - if ((flags & FileUtil.BA_REGULAR) != 0) { - result |= (info.attributes & (FileInfo.FILE_ATTRIBUTE_DIRECTORY | FileInfo.FILE_ATTRIBUTE_DEVICE | FileInfo.FILE_ATTRIBUTE_REPARSE_POINT)) != 0 - ? 0 : FileUtil.BA_REGULAR; - } - if ((flags & FileUtil.BA_HIDDEN) != 0) { - result |= (info.attributes & FileInfo.FILE_ATTRIBUTE_HIDDEN) == 0 ? 0 : FileUtil.BA_HIDDEN; - } - return result; - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java index e1cd15859bdb..c3711490f8a5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java @@ -66,9 +66,9 @@ public class Win32LocalFileSystem extends LocalFileSystemBase { } @Override - public boolean exists(@NotNull VirtualFile fileOrDirectory) { - if (fileOrDirectory.getParent() == null) return true; - return myFsCache.getInfo(fileOrDirectory) != null; + public boolean exists(@NotNull VirtualFile file) { + if (file.getParent() == null) return true; + return myFsCache.getInfo(file) != null; } @Override @@ -83,6 +83,18 @@ public class Win32LocalFileSystem extends LocalFileSystemBase { return fileInfo != null && notSet(fileInfo.attributes, FileInfo.FILE_ATTRIBUTE_READONLY); } + @Override + public boolean isSymLink(@NotNull VirtualFile file) { + final FileInfo fileInfo = myFsCache.getInfo(file); + return fileInfo != null && isSet(fileInfo.attributes, FileInfo.FILE_ATTRIBUTE_REPARSE_POINT); + } + + @Override + public boolean isSpecialFile(@NotNull VirtualFile file) { + final FileInfo fileInfo = myFsCache.getInfo(file); + return fileInfo != null && isSet(fileInfo.attributes, FileInfo.FILE_ATTRIBUTE_DEVICE); + } + @Override public long getTimeStamp(@NotNull VirtualFile file) { final FileInfo fileInfo = myFsCache.getInfo(file); @@ -112,9 +124,4 @@ public class Win32LocalFileSystem extends LocalFileSystemBase { @Nullable Collection flatRoots) { throw new UnsupportedOperationException(); } - - @Override - public int getBooleanAttributes(@NotNull VirtualFile file, int flags) { - return myFsCache.getBooleanAttributes(file, flags); - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java index 9a52ab4fadf3..ac29b49eb1c7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java @@ -25,6 +25,7 @@ import com.intellij.openapi.roots.OrderEnumerator; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; @@ -311,12 +312,11 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { @Nullable private VirtualFileSystemEntry createAndFindChildWithEventFire(@NotNull String name) { final NewVirtualFileSystem delegate = getFileSystem(); - VirtualFile fake = new FakeVirtualFile(this, name); - int attributes = delegate.getBooleanAttributes(fake, FileUtil.BA_EXISTS | FileUtil.BA_DIRECTORY); - if ((attributes & FileUtil.BA_EXISTS) != 0) { + final VirtualFile fake = new FakeVirtualFile(this, name); + final FileAttributes attributes = delegate.getAttributes(fake); + if (attributes != null) { final String realName = delegate.getCanonicallyCasedName(fake); - boolean isDir = (attributes & FileUtil.BA_DIRECTORY) != 0; - VFileCreateEvent event = new VFileCreateEvent(null, this, realName, isDir, true); + final VFileCreateEvent event = new VFileCreateEvent(null, this, realName, attributes.isDirectory(), true); RefreshQueue.getInstance().processSingleEvent(event); return findChild(realName); } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java index e08ab5b4892c..56a28bf833db 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java @@ -19,7 +19,6 @@ import com.intellij.ide.ui.UISettings; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.ThrowableComputable; -import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileTooBigException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -87,8 +86,9 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile { } private void calcLinkStatus() { - putUserData(SYMLINK_TARGET, isSymLink() ? FileSystemUtil.resolveSymLink(getPath()) : null); - setFlagInt(HAS_SYMLINK_FLAG, isSymLink() || ((VirtualFileSystemEntry)myParent).getFlagInt(HAS_SYMLINK_FLAG)); + boolean symLink = isSymLink(); + putUserData(SYMLINK_TARGET, symLink ? myParent.getFileSystem().resolveSymLink(this) : null); + setFlagInt(HAS_SYMLINK_FLAG, symLink || ((VirtualFileSystemEntry)myParent).getFlagInt(HAS_SYMLINK_FLAG)); } private static Object encodeName(@NotNull String name) { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java index b79abe57b20b..2e863dd6b324 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java @@ -23,10 +23,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.ShutDownTracker; import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream; -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; -import com.intellij.openapi.util.io.ByteSequence; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.*; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.impl.win32.Win32LocalFileSystem; @@ -189,9 +186,10 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { childrenIds[i] = currentIds[idx]; } else { - FakeVirtualFile child = new FakeVirtualFile(file, name); - int attributes = delegate.getBooleanAttributes(child, -1); - int childId = createAndCopyRecord(delegate, child, id, attributes); + final FakeVirtualFile child = new FakeVirtualFile(file, name); + final FileAttributes attributes = delegate.getAttributes(child); + assert attributes != null : delegate + ": " + child; + final int childId = createAndCopyRecord(delegate, child, id, attributes); childrenIds[i] = childId; } } @@ -289,32 +287,26 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { private static boolean copyRecordFromDelegateFS(final int id, final int parentId, @NotNull VirtualFile file, - @NotNull NewVirtualFileSystem delegate, - @FileUtil.FileBooleanAttributes int attributes) { + @NotNull NewVirtualFileSystem fs, + @NotNull FileAttributes attributes) { String name = file.getName(); - - if (!name.isEmpty() && namesEqual(delegate, name, FSRecords.getName(id))) return false; // TODO: Handle root attributes change. - - if (name.isEmpty()) { // TODO: hack - if (areChildrenLoaded(id)) return false; + if (!name.isEmpty()) { + if (namesEqual(fs, name, FSRecords.getName(id))) return false; // TODO: Handle root attributes change. + } + else { + if (areChildrenLoaded(id)) return false; // TODO: hack } FSRecords.setParent(id, parentId); FSRecords.setName(id, name); - delegate = replaceWithNativeFS(delegate); + FSRecords.setTimestamp(id, attributes.lastModified); + FSRecords.setLength(id, attributes.isDirectory() ? -1L : attributes.length); - FSRecords.setTimestamp(id, delegate.getTimeStamp(file)); - - boolean isDir = (attributes & FileUtil.BA_DIRECTORY) != 0; - FSRecords.setLength(id, isDir ? -1L : delegate.getLength(file)); - - boolean isSpecial = (attributes & (FileUtil.BA_REGULAR | FileUtil.BA_DIRECTORY | FileUtil.BA_EXISTS)) == - FileUtil.BA_EXISTS; - FSRecords.setFlags(id, (isDir ? IS_DIRECTORY_FLAG : 0) | - (delegate.isWritable(file) ? 0 : IS_READ_ONLY) | - (delegate.isSymLink(file) ? IS_SYMLINK : 0) | - (isSpecial ? IS_SPECIAL : 0), true); + FSRecords.setFlags(id, (attributes.isDirectory() ? IS_DIRECTORY_FLAG : 0) | + (attributes.isWritable() ? 0 : IS_READ_ONLY) | + (attributes.isSymLink() ? IS_SYMLINK : 0) | + (attributes.isSpecial() ? IS_SPECIAL : 0), true); return true; } @@ -404,10 +396,10 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { if (namesEqual(delegate, childName, FSRecords.getName(childId))) return childId; } - VirtualFile fake = new FakeVirtualFile(parent, childName); - int attributes = delegate.getBooleanAttributes(fake, -1); - if ((attributes & FileUtil.BA_EXISTS) != 0) { - int child = createAndCopyRecord(delegate, fake, parentId, attributes); + final VirtualFile fake = new FakeVirtualFile(parent, childName); + final FileAttributes attributes = delegate.getAttributes(fake); + if (attributes != null) { + final int child = createAndCopyRecord(delegate, fake, parentId, attributes); FSRecords.updateList(parentId, ArrayUtil.append(children, child)); return child; } @@ -764,6 +756,7 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { if (basePath.isEmpty()) { // fake root for windows root = new VirtualDirectoryImpl("", null, fs, rootId) { + @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") @Override @NotNull public VirtualFile[] getChildren() { @@ -789,14 +782,15 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { root = new VirtualDirectoryImpl(basePath, null, fs, rootId); } } - if (!fs.exists(root)) { + + final FileAttributes attributes = fs.getAttributes(root); + if (attributes == null) { return null; } - int attributes = FileUtil.BA_DIRECTORY | FileUtil.BA_EXISTS; - boolean newRoot = copyRecordFromDelegateFS(rootId, 0, root, fs, attributes); + final boolean newRoot = copyRecordFromDelegateFS(rootId, 0, root, fs, attributes); if (!newRoot) { - if (fs.getTimeStamp(root) != FSRecords.getTimestamp(rootId)) { + if (attributes.lastModified != FSRecords.getTimestamp(rootId)) { root.markDirtyRecursively(); } } @@ -981,11 +975,11 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { private static void executeCreateChild(@NotNull VirtualFile parent, @NotNull String name) { final NewVirtualFileSystem delegate = getDelegate(parent); - VirtualFile fake = new FakeVirtualFile(parent, name); - int attributes = delegate.getBooleanAttributes(fake, -1); - if ((attributes & FileUtil.BA_EXISTS) != 0) { + final VirtualFile fake = new FakeVirtualFile(parent, name); + final FileAttributes attributes = delegate.getAttributes(fake); + if (attributes != null) { final int parentId = getFileId(parent); - int childId = createAndCopyRecord(delegate, fake, parentId, attributes); + final int childId = createAndCopyRecord(delegate, fake, parentId, attributes); appendIdToParentList(parentId, childId); assert parent instanceof VirtualDirectoryImpl : parent; final VirtualDirectoryImpl dir = (VirtualDirectoryImpl)parent; @@ -993,8 +987,11 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { } } - private static int createAndCopyRecord(@NotNull NewVirtualFileSystem delegateSystem, @NotNull VirtualFile delegateFile, int parentId, @FileUtil.FileBooleanAttributes int attributes) { - int childId = FSRecords.createRecord(); + private static int createAndCopyRecord(@NotNull NewVirtualFileSystem delegateSystem, + @NotNull VirtualFile delegateFile, + int parentId, + @NotNull FileAttributes attributes) { + final int childId = FSRecords.createRecord(); copyRecordFromDelegateFS(childId, parentId, delegateFile, delegateSystem, attributes); return childId; } @@ -1097,8 +1094,9 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { } final NewVirtualFileSystem delegate = getDelegate(file); - FSRecords.setLength(getFileId(file), delegate.getLength(file)); - FSRecords.setTimestamp(getFileId(file), delegate.getTimeStamp(file)); + final FileAttributes attributes = delegate.getAttributes(file); + FSRecords.setLength(getFileId(file), attributes != null ? attributes.length : DEFAULT_LENGTH); + FSRecords.setTimestamp(getFileId(file), attributes != null ? attributes.lastModified : DEFAULT_TIMESTAMP); ((VirtualFileSystemEntry)file).setModificationStamp(newModificationStamp); } 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 e07854bc140c..99e03336ee61 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 @@ -16,8 +16,7 @@ package com.intellij.openapi.vfs.newvfs.persistent; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.ManagingFS; @@ -36,152 +35,149 @@ import java.util.*; * @author max */ public class RefreshWorker { - private final VirtualFile myRefreshRoot; private final boolean myIsRecursive; private final Queue myRefreshQueue = new Queue(100); - private final List myEvents = new ArrayList(); public RefreshWorker(final VirtualFile refreshRoot, final boolean isRecursive) { - myRefreshRoot = refreshRoot; myIsRecursive = isRecursive; myRefreshQueue.addLast(refreshRoot); } public void scan() { - final NewVirtualFile root = (NewVirtualFile)myRefreshRoot; - NewVirtualFileSystem delegate = root.getFileSystem(); - final int rootAttributes = delegate.getBooleanAttributes(root, -1); + final NewVirtualFile root = (NewVirtualFile)myRefreshQueue.peekFirst(); + if (!root.isDirty()) return; - if (root.isDirty() && (rootAttributes & FileUtil.BA_EXISTS) == 0) { + NewVirtualFileSystem fs = root.getFileSystem(); + final FileAttributes rootAttributes = fs.getAttributes(root); + + if (rootAttributes == null) { scheduleDeletion(root); root.markClean(); + return; } - else { - if ((rootAttributes & FileUtil.BA_DIRECTORY) != 0) { - delegate = PersistentFS.replaceWithNativeFS(delegate); + + if (rootAttributes != null && rootAttributes.isDirectory()) { + fs = PersistentFS.replaceWithNativeFS(fs); + } + + final PersistentFS persistence = (PersistentFS)ManagingFS.getInstance(); + + while (!myRefreshQueue.isEmpty()) { + final VirtualFileSystemEntry file = (VirtualFileSystemEntry)myRefreshQueue.pullFirst(); + if (!file.isDirty()) continue; + + final FileAttributes attributes = Comparing.equal(file, root) ? rootAttributes : fs.getAttributes(file); + if (attributes == null) { + scheduleDeletion(file); + continue; } - final PersistentFS persistence = (PersistentFS)ManagingFS.getInstance(); + boolean checkFurther = true; + final VirtualFileSystemEntry parent = file.getParent(); + if (parent != null && checkAndScheduleAttributesChange(parent, file, attributes)) { + // ignore everything else + checkFurther = false; + } + else if (file.isDirectory()) { + final VirtualDirectoryImpl dir = (VirtualDirectoryImpl)file; + final boolean fullSync = dir.allChildrenLoaded(); + if (fullSync) { + final Set currentNames = new HashSet(Arrays.asList(persistence.list(file))); + final Set upToDateNames = new HashSet(Arrays.asList(VfsUtil.filterNames(fs.list(file)))); - while (!myRefreshQueue.isEmpty()) { - final VirtualFileSystemEntry file = (VirtualFileSystemEntry)myRefreshQueue.pullFirst(); - if (!file.isDirty()) continue; + final Set newNames = new HashSet(upToDateNames); + newNames.removeAll(currentNames); - int attributes = Comparing.equal(file, root) ? rootAttributes : delegate.getBooleanAttributes(file, -1); - VirtualFileSystemEntry parent = file.getParent(); - if (parent != null && checkAndScheduleAttributesChange(parent, file, delegate, attributes)) { - // ignore everything else - } - else if (file.isDirectory()) { - final VirtualDirectoryImpl dir = (VirtualDirectoryImpl)file; - final boolean fullSync = dir.allChildrenLoaded(); - if (fullSync) { - final Set currentNames = new HashSet(Arrays.asList(persistence.list(file))); - final Set upToDateNames = new HashSet(Arrays.asList(VfsUtil.filterNames(delegate.list(file)))); + final Set deletedNames = new HashSet(currentNames); + deletedNames.removeAll(upToDateNames); - final Set newNames = new HashSet(upToDateNames); - newNames.removeAll(currentNames); - - final Set deletedNames = new HashSet(currentNames); - deletedNames.removeAll(upToDateNames); - - for (String name : deletedNames) { - scheduleDeletion(file.findChild(name)); - } - - for (String name : newNames) { - boolean isDirectory = delegate.isDirectory(new FakeVirtualFile(file, name)); - scheduleCreation(file, name, isDirectory); - } - - for (VirtualFile child : file.getChildren()) { - if (!deletedNames.contains(child.getName())) { - final int childAttributes = delegate.getBooleanAttributes(child, -1); - scheduleChildRefresh(file, child, delegate, childAttributes); - } - } + for (String name : deletedNames) { + scheduleDeletion(file.findChild(name)); } - else { - for (VirtualFile child : file.getCachedChildren()) { - final int childAttributes = delegate.getBooleanAttributes(child, -1); - if ((childAttributes & FileUtil.BA_EXISTS) != 0) { - scheduleChildRefresh(file, child, delegate, childAttributes); - } - else { - scheduleDeletion(child); - } - } - final List names = dir.getSuspiciousNames(); - for (String name : names) { - if (name.isEmpty()) continue; + for (String name : newNames) { + boolean isDirectory = fs.isDirectory(new FakeVirtualFile(file, name)); + scheduleCreation(file, name, isDirectory); + } - final VirtualFile fake = new FakeVirtualFile(file, name); - final int childAttributes = delegate.getBooleanAttributes(fake, FileUtil.BA_EXISTS | FileUtil.BA_DIRECTORY); - if ((childAttributes & FileUtil.BA_EXISTS) != 0) { - final boolean isDir = (childAttributes & FileUtil.BA_DIRECTORY) != 0; - scheduleCreation(file, name, isDir); - } + for (VirtualFile child : file.getChildren()) { + if (!deletedNames.contains(child.getName())) { + checkAndScheduleChildRefresh(file, child, fs.getAttributes(child)); } } } else { - long currentTimestamp = persistence.getTimeStamp(file); - long upToDateTimestamp = delegate.getTimeStamp(file); - long currentLength = SystemInfo.isUnix ? persistence.getLengthNoFollow(file) : -1; - long upToDateLength = SystemInfo.isUnix ? delegate.getLength(file) : -1; + for (VirtualFile child : file.getCachedChildren()) { + final FileAttributes childAttributes = fs.getAttributes(child); + if (childAttributes != null) { + checkAndScheduleChildRefresh(file, child, childAttributes); + } + else { + scheduleDeletion(child); + } + } - if (currentTimestamp != upToDateTimestamp || currentLength != upToDateLength) { - scheduleUpdateContent(file); + final List names = dir.getSuspiciousNames(); + for (String name : names) { + if (name.isEmpty()) continue; + + final VirtualFile fake = new FakeVirtualFile(file, name); + final FileAttributes childAttributes = fs.getAttributes(fake); + if (childAttributes != null) { + scheduleCreation(file, name, childAttributes.isDirectory()); + } } } + } + else { + final long currentTimestamp = persistence.getTimeStamp(file); + final long upToDateTimestamp = attributes.lastModified; + final long currentLength = persistence.getLengthNoFollow(file); + final long upToDateLength = attributes.length; - boolean currentWritable = persistence.isWritable(file); - boolean upToDateWritable = delegate.isWritable(file); + if (currentTimestamp != upToDateTimestamp || currentLength != upToDateLength) { + scheduleUpdateContent(file); + } + } + + if (checkFurther) { + final boolean currentWritable = persistence.isWritable(file); + final boolean upToDateWritable = attributes.isWritable(); if (currentWritable != upToDateWritable) { scheduleWritableAttributeChange(file, currentWritable, upToDateWritable); } - - file.markClean(); } + + file.markClean(); } } - private static final int SPECIAL_MASK = FileUtil.BA_REGULAR | FileUtil.BA_DIRECTORY | FileUtil.BA_EXISTS; - - // todo[r.sh] compare link targets for files too - private void scheduleChildRefresh(@NotNull VirtualFileSystemEntry parent, - @NotNull VirtualFile child, - @NotNull NewVirtualFileSystem delegate, - @FileUtil.FileBooleanAttributes int childAttributes) { - if (!checkAndScheduleAttributesChange(parent, child, delegate, childAttributes)) { - boolean upToDateIsDirectory = (childAttributes & FileUtil.BA_DIRECTORY) != 0; + private void checkAndScheduleChildRefresh(@NotNull VirtualFileSystemEntry parent, + @NotNull VirtualFile child, + @NotNull FileAttributes childAttributes) { + if (!checkAndScheduleAttributesChange(parent, child, childAttributes)) { + final boolean upToDateIsDirectory = childAttributes.isDirectory(); if (myIsRecursive || !upToDateIsDirectory) { myRefreshQueue.addLast(child); } } } - // returns true if change was detected and events scheduled private boolean checkAndScheduleAttributesChange(@NotNull VirtualFileSystemEntry parent, @NotNull VirtualFile child, - @NotNull NewVirtualFileSystem delegate, - @FileUtil.FileBooleanAttributes int childAttributes) { + @NotNull FileAttributes childAttributes) { final boolean currentIsDirectory = child.isDirectory(); final boolean currentIsSymlink = child.isSymLink(); final boolean currentIsSpecial = child.isSpecialFile(); - //final String currentLinkTarget = child instanceof SymlinkDirectory ? ((SymlinkDirectory)child).getTargetPath() : null; - final boolean upToDateIsDirectory = (childAttributes & FileUtil.BA_DIRECTORY) != 0; - final boolean upToDateIsSymlink = delegate.isSymLink(child); - final boolean upToDateIsSpecial = (childAttributes & SPECIAL_MASK) == FileUtil.BA_EXISTS; - //final String upToDateLinkTarget = currentLinkTarget != null ? delegate.resolveSymLink(child) : null; + final boolean upToDateIsDirectory = childAttributes.isDirectory(); + final boolean upToDateIsSymlink = childAttributes.isSymLink(); + final boolean upToDateIsSpecial = child.isSpecialFile(); if (currentIsDirectory != upToDateIsDirectory || currentIsSymlink != upToDateIsSymlink || - currentIsSpecial != upToDateIsSpecial /*|| - !Comparing.equal(currentLinkTarget, upToDateLinkTarget)*/) { + currentIsSpecial != upToDateIsSpecial) { scheduleDeletion(child); scheduleReCreation(parent, child.getName(), upToDateIsDirectory); return true; 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 88995e68bd6d..6eff32f81659 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 @@ -29,6 +29,7 @@ import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem; import com.intellij.openapi.vfs.newvfs.RefreshQueue; import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent; import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; +import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; import com.intellij.testFramework.PlatformLangTestCase; import com.intellij.testFramework.PlatformTestUtil; @@ -245,10 +246,7 @@ public class LocalFileSystemTest extends PlatformLangTestCase { assertNotNull(virtualFile); NewVirtualFileSystem fs = (NewVirtualFileSystem)virtualFile.getFileSystem(); - fs = PersistentFS.replaceWithNativeFS(fs); - - assertTrue(fs.exists(virtualFile)); - final FileAttributes attributes = fs.getAttributes(virtualFile); + FileAttributes attributes = fs.getAttributes(virtualFile); assertNotNull(attributes); assertEquals(FileAttributes.Type.FILE, attributes.type); assertEquals(FileAttributes.HIDDEN, attributes.flags); @@ -285,4 +283,21 @@ public class LocalFileSystemTest extends PlatformLangTestCase { } assertEquals(expectedCount, children.length); } + + public void testSingleFileRootRefresh() throws Exception { + File file = FileUtil.createTempFile("test.", ".txt"); + VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + assertNotNull(virtualFile); + assertTrue(virtualFile.exists()); + assertTrue(virtualFile.isValid()); + + virtualFile.refresh(false, false); + assertFalse(((VirtualFileSystemEntry)virtualFile).isDirty()); + + FileUtil.delete(file); + assertFalse(file.exists()); + virtualFile.refresh(false, false); + assertFalse(virtualFile.exists()); + assertFalse(virtualFile.isValid()); + } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java index 1d5ba117808a..d067c3df6d94 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java @@ -54,21 +54,33 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase { } } - public void testBadLinksAreIgnored() throws Exception { + public void testBadLinks() throws Exception { final File missingFile = new File(FileUtil.getTempDirectory(), "missing_file"); assertTrue(missingFile.getAbsolutePath(), !missingFile.exists() || missingFile.delete()); final File missingLinkFile = createTempLink(missingFile.getAbsolutePath(), "missing_link"); final VirtualFile missingLinkVFile = refreshAndFind(missingLinkFile); - assertNull(missingLinkVFile); + assertBrokenLink(missingLinkVFile); final File selfLinkFile = createTempLink("self_link", "self_link"); final VirtualFile selfLinkVFile = refreshAndFind(selfLinkFile); - assertNull(selfLinkVFile); + assertBrokenLink(selfLinkVFile); final File pointLinkFile = createTempLink(".", "point_link"); final VirtualFile pointLinkVFile = refreshAndFind(pointLinkFile); assertNotNull(pointLinkVFile); + assertTrue(pointLinkVFile.isSymLink()); + assertTrue(pointLinkVFile.isDirectory()); assertEquals(0, pointLinkVFile.getChildren().length); + System.out.println(pointLinkVFile.getCanonicalPath()); + + final File upLinkDir = FileUtil.createTempDirectory("dir1.", null); + final File upLinkFile = createTempLink(upLinkDir.getAbsolutePath(), new File(upLinkDir, "up_link").getAbsolutePath()); + final VirtualFile upLinkVFile = refreshAndFind(upLinkFile); + assertNotNull(upLinkVFile); + assertTrue(upLinkVFile.isSymLink()); + assertTrue(upLinkVFile.isDirectory()); + assertEquals(0, upLinkVFile.getChildren().length); + System.out.println(upLinkVFile.getCanonicalPath()); final File circularDir1 = FileUtil.createTempDirectory("dir1.", null); final File circularDir2 = FileUtil.createTempDirectory("dir2.", null); @@ -84,6 +96,13 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase { assertEquals(0, circularLink2VFile.getChildren()[0].getChildren().length); } + private static void assertBrokenLink(@Nullable VirtualFile link) { + assertNotNull(link); + assertTrue(link.isSymLink()); + assertEquals(0, link.getLength()); + assertNull(link.getCanonicalPath(), link.getCanonicalPath()); + } + public void testTargetIsWritable() throws Exception { final File targetFile = FileUtil.createTempFile("target", ""); final File linkFile = createTempLink(targetFile.getAbsolutePath(), "link"); diff --git a/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java b/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java index c6a83a5074b5..9cf61e796642 100644 --- a/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java +++ b/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java @@ -241,11 +241,6 @@ public class MockLocalFileSystem extends LocalFileSystem { return 1; } - @Override - public int getBooleanAttributes(@NotNull VirtualFile file, int flags) { - return 0; - } - @Override public FileAttributes getAttributes(@NotNull VirtualFile file) { return null; diff --git a/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java b/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java index c2b6873ae11e..c514dd95de1c 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java @@ -123,6 +123,18 @@ public final class FileAttributes { return flags; } + public boolean isFile() { + return type == Type.FILE; + } + + public boolean isDirectory() { + return type == Type.DIRECTORY; + } + + public boolean isSpecial() { + return type == Type.SPECIAL; + } + public boolean isSymLink() { return isSet(flags, SYM_LINK); } diff --git a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java index 337119cc0fa2..97757a42f73c 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java @@ -411,7 +411,7 @@ public class FileSystemUtil { private JnaUnixMediatorImpl() throws Exception { myLibC = (LibC)Native.loadLibrary("c", LibC.class); - mySharedMem = new Memory(512); + mySharedMem = new Memory(256); myModeOffset = SystemInfo.isLinux ? (SystemInfo.is32Bit ? 16 : 24) : SystemInfo.isMac | SystemInfo.isFreeBSD ? 8 : SystemInfo.isSolaris ? (SystemInfo.is32Bit ? 20 : 16) : @@ -436,7 +436,6 @@ public class FileSystemUtil { int mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK; final boolean isSymlink = (mode & LibC.S_IFLNK) == LibC.S_IFLNK; if (isSymlink) { - mySharedMem.clear(); res = SystemInfo.isLinux ? myLibC.__xstat64(0, path, mySharedMem) : myLibC.stat(path, mySharedMem); if (res != 0) { return FileAttributes.BROKEN_SYMLINK; diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index 36ab8a7096b9..0d55074871b6 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -28,7 +28,6 @@ import com.intellij.util.containers.Stack; import com.intellij.util.io.URLUtil; import com.intellij.util.text.CaseInsensitiveStringHashingStrategy; import gnu.trove.TObjectHashingStrategy; -import org.intellij.lang.annotations.MagicConstant; import org.intellij.lang.annotations.RegExp; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -396,20 +395,16 @@ public class FileUtil extends FileUtilRt { public static boolean createParentDirs(@NotNull File file) { if (!file.exists()) { - String parentDirPath = file.getParent(); - if (parentDirPath != null) { - final File parentFile = new File(parentDirPath); + final File parentFile = file.getParentFile(); + if (parentFile != null) { return createDirectory(parentFile); } } return true; } - public static boolean createDirectory(File parentFile) { - int parentAttributes = getBooleanAttributes(parentFile); - boolean ok = parentAttributes != -1 ? (parentAttributes & (BA_EXISTS | BA_DIRECTORY)) == (BA_EXISTS | BA_DIRECTORY) - : parentFile.exists() && parentFile.isDirectory(); - return ok || parentFile.mkdirs(); + public static boolean createDirectory(@NotNull File path) { + return path.isDirectory() || path.mkdirs(); } public static boolean createIfDoesntExist(@NotNull File file) { @@ -1008,12 +1003,13 @@ public class FileUtil extends FileUtilRt { @NotNull public static List findFilesByMask(@NotNull Pattern pattern, @NotNull File dir) { final ArrayList found = new ArrayList(); - for (File file : dir.listFiles()) { - if (file.isDirectory()) { - found.addAll(findFilesByMask(pattern, file)); - } - else { - if (pattern.matcher(file.getName()).matches()) { + final File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + found.addAll(findFilesByMask(pattern, file)); + } + else if (pattern.matcher(file.getName()).matches()) { found.add(file); } } @@ -1024,13 +1020,15 @@ public class FileUtil extends FileUtilRt { @NotNull public static List findFilesOrDirsByMask(@NotNull Pattern pattern, @NotNull File dir) { final ArrayList found = new ArrayList(); - for (File file : dir.listFiles()) { - if (file.isDirectory()) { - found.addAll(findFilesOrDirsByMask(pattern, file)); - } - - if (pattern.matcher(file.getName()).matches()) { - found.add(file); + final File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (pattern.matcher(file.getName()).matches()) { + found.add(file); + } + if (file.isDirectory()) { + found.addAll(findFilesOrDirsByMask(pattern, file)); + } } } return found; @@ -1039,7 +1037,7 @@ public class FileUtil extends FileUtilRt { /** * Returns empty string for empty path. * First checks whether provided path is a path of a file with sought-for name. - * Unless found, checks if provided file was a directory. In this case checks existance + * Unless found, checks if provided file was a directory. In this case checks existence * of child files with given names in order "as provided". Finally checks filename among * brother-files of provided. Returns null if nothing found. * @@ -1152,7 +1150,7 @@ public class FileUtil extends FileUtilRt { @NotNull public static File createTempDirectory(@NotNull File dir, @NotNull @NonNls String prefix, @Nullable @NonNls String suffix, boolean deleteOnExit) throws IOException { - return FileUtilRt.createTempDirectory(dir, prefix, suffix,deleteOnExit); + return FileUtilRt.createTempDirectory(dir, prefix, suffix, deleteOnExit); } @NotNull @@ -1243,58 +1241,6 @@ public class FileUtil extends FileUtilRt { return FileUtilRt.loadBytes(stream, length); } - // copied from FileSystem: they are package local there - public static final int BA_EXISTS = 0x01; - public static final int BA_REGULAR = 0x02; - public static final int BA_DIRECTORY = 0x04; - public static final int BA_HIDDEN = 0x08; - - @MagicConstant(flags = {BA_EXISTS, BA_REGULAR, BA_DIRECTORY, BA_HIDDEN}) - public @interface FileBooleanAttributes {} - - private static final Method JAVA_IO_FILESYSTEM_GET_BOOLEAN_ATTRIBUTES_METHOD; - private static final Object JAVA_IO_FILESYSTEM; - - // todo[r.sh] use NIO2 API after migration to JDK 7 - // returns -1 if could not get attributes - @FileBooleanAttributes - public static int getBooleanAttributes(@NotNull File f) { - if (JAVA_IO_FILESYSTEM_GET_BOOLEAN_ATTRIBUTES_METHOD != null) { - try { - Object flags = JAVA_IO_FILESYSTEM_GET_BOOLEAN_ATTRIBUTES_METHOD.invoke(JAVA_IO_FILESYSTEM, f); - //noinspection MagicConstant - return ((Integer)flags).intValue(); - } - catch (Exception ignored) { } - } - return -1; - } - - static { - Object fs; - Method getBooleanAttributes; - try { - Class fsClass = Class.forName("java.io.FileSystem"); - Method getFileSystem = fsClass.getMethod("getFileSystem"); - getFileSystem.setAccessible(true); - fs = getFileSystem.invoke(null); - getBooleanAttributes = fsClass.getDeclaredMethod("getBooleanAttributes", File.class); - if (fs == null || getBooleanAttributes == null) { - fs = null; - getBooleanAttributes = null; - } - else { - getBooleanAttributes.setAccessible(true); - } - } - catch (Exception e) { - fs = null; - getBooleanAttributes = null; - } - JAVA_IO_FILESYSTEM = fs; - JAVA_IO_FILESYSTEM_GET_BOOLEAN_ATTRIBUTES_METHOD = getBooleanAttributes; - } - private static final class CompletedFuture implements Future { public boolean cancel(boolean mayInterruptIfRunning) { return false; @@ -1305,9 +1251,11 @@ public class FileUtil extends FileUtilRt { public boolean isDone() { return true; } + @Nullable public T get() throws InterruptedException, ExecutionException { return null; } + @Nullable public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; }