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 14b5b258be37..4ef1fae4a59a 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 @@ -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. @@ -16,6 +16,7 @@ package com.intellij.openapi.vfs.impl.jar; 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.VirtualFile; import com.intellij.util.ArrayUtil; @@ -224,4 +225,12 @@ public class JarHandlerBase { return getEntryInfo(fileOrDirectory) != null; } + + @Nullable + public FileAttributes getAttributes(@NotNull final VirtualFile file) { + synchronized (lock) { + final ZipEntry entry = convertToEntry(file); + return entry != null ? new FileAttributes(entry.isDirectory(), false, false, entry.getSize(), entry.getTime(), false) : null; + } + } } diff --git a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java index 62ea6513fb23..f4f60aea8dca 100644 --- a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java +++ b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.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. @@ -113,9 +113,10 @@ public class JdkUtil { return null; } - public static boolean checkForJdk(File file) { - file = new File(file.getAbsolutePath() + File.separator + "bin"); - if (!file.exists()) return false; + public static boolean checkForJdk(final File homePath) { + File binPath = new File(homePath.getAbsolutePath() + File.separator + "bin"); + if (!binPath.exists()) return false; + FileFilter fileFilter = new FileFilter() { @SuppressWarnings({"HardCodedStringLiteral"}) public boolean accept(File f) { @@ -124,21 +125,34 @@ public class JdkUtil { Comparing.strEqual(FileUtil.getNameWithoutExtension(f), "javah"); } }; - File[] children = file.listFiles(fileFilter); - return children != null && children.length >= 2; + File[] children = binPath.listFiles(fileFilter); + + return children != null && children.length >= 2 && + checkForRuntime(homePath.getAbsolutePath()); } - public static boolean checkForJre(String file) { - File ioFile = new File(new File(file.replace('/', File.separatorChar)).getAbsolutePath() + File.separator + "bin"); - if (!ioFile.exists()) return false; + public static boolean checkForJre(String homePath) { + homePath = new File(FileUtil.toSystemDependentName(homePath)).getAbsolutePath(); + File binPath = new File(homePath + File.separator + "bin"); + if (!binPath.exists()) return false; + FileFilter fileFilter = new FileFilter() { @SuppressWarnings({"HardCodedStringLiteral"}) public boolean accept(File f) { return !f.isDirectory() && Comparing.strEqual(FileUtil.getNameWithoutExtension(f), "java"); } }; - File[] children = ioFile.listFiles(fileFilter); - return children != null && children.length >= 1; + File[] children = binPath.listFiles(fileFilter); + + return children != null && children.length >= 1 && + checkForRuntime(homePath); + } + + public static boolean checkForRuntime(final String homePath) { + return new File(homePath + File.separator + "jre" + File.separator + "lib" + File.separator + "rt.jar").exists() || + new File(homePath + File.separator + "lib" + File.separator + "rt.jar").exists() || + new File(homePath + File.separator + ".." + File.separator + "Classes" + File.separator + "classes.jar").exists() || + new File(homePath + File.separator + "jre" + File.separator + "lib" + File.separator + "vm.jar").exists(); } public static GeneralCommandLine setupJVMCommandLine(final String exePath, diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFile.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFile.java index e7f64e01d003..aadf5db23de2 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFile.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/NewVirtualFile.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * @author max - */ package com.intellij.openapi.vfs.newvfs; import com.intellij.openapi.application.ApplicationManager; @@ -31,9 +27,13 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Collection; +import java.util.concurrent.atomic.AtomicLong; +/** + * @author max + */ public abstract class NewVirtualFile extends VirtualFile implements VirtualFileWithId { - private volatile long myModificationStamp = LocalTimeCounter.currentTime(); + private final AtomicLong myModificationStamp = new AtomicLong(LocalTimeCounter.currentTime()); public boolean isValid() { ApplicationManager.getApplication().assertReadAccessAllowed(); @@ -50,6 +50,9 @@ public abstract class NewVirtualFile extends VirtualFile implements VirtualFileW public abstract NewVirtualFile getParent(); + @Nullable + public abstract NewVirtualFile getCanonicalFile(); + @Nullable public abstract NewVirtualFile findChild(@NotNull @NonNls final String name); @@ -75,11 +78,11 @@ public abstract class NewVirtualFile extends VirtualFile implements VirtualFileW } public long getModificationStamp() { - return myModificationStamp; + return myModificationStamp.get(); } public void setModificationStamp(long modificationStamp) { - myModificationStamp = modificationStamp; + myModificationStamp.set(modificationStamp); } public abstract void setWritable(boolean writable) throws IOException; 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 b7f274080b78..a132ea30cdab 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 @@ -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. @@ -14,11 +14,9 @@ * limitations under the License. */ -/* - * @author max - */ 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; @@ -32,6 +30,9 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +/** + * @author max + */ public abstract class NewVirtualFileSystem extends VirtualFileSystem implements FileSystemInterface, CachingVirtualFileSystem { private final Map myListenerWrappers = new HashMap(); @@ -150,4 +151,14 @@ public abstract class NewVirtualFileSystem extends VirtualFileSystem implements */ @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). + * + * @param file file to get attributes of. + * @return attributes of a given file, or null if the file doesn't exist. + * @since 11.1 + */ + @Nullable + public abstract FileAttributes getAttributes(@NotNull VirtualFile file); } 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 c1da943f68a8..1b9a3bae4eb9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -14,13 +14,11 @@ * limitations under the License. */ -/* - * @author max - */ 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; @@ -40,6 +38,9 @@ import java.io.*; import java.util.ArrayList; import java.util.List; +/** + * @author max + */ public class TempFileSystem extends NewVirtualFileSystem { private final FSItem myRoot = new FSDir(null, "/"); @@ -375,4 +376,12 @@ public class TempFileSystem extends NewVirtualFileSystem { 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); + if (item == null) return null; + final long length = item instanceof FSFile ? ((FSFile)item).myContent.length : 0; + return new FileAttributes(item.isDirectory(), false, false, length, item.myTimestamp, item.myWritable); + } } 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 c7c451895759..c7b85297e3f1 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -21,6 +21,7 @@ import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ApplicationComponent; 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.text.StringUtil; import com.intellij.openapi.vfs.*; @@ -381,4 +382,19 @@ public class JarFileSystemImpl extends JarFileSystem implements ApplicationCompo } return exists | isDir | regular; } + + @Override + public FileAttributes getAttributes(@NotNull final VirtualFile file) { + final JarHandler handler = getHandler(file); + if (handler == null) return null; + + 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 handler.getAttributes(file); + } } 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 201ff050ba98..baa358304c19 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 @@ -22,6 +22,8 @@ import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileAttributes; +import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; @@ -45,17 +47,14 @@ import java.io.IOException; import java.util.*; public final class LocalFileSystemImpl extends LocalFileSystemBase implements ApplicationComponent { - private final JBReentrantReadWriteLock LOCK = LockFactory.createReadWriteLock(); - final JBLock WRITE_LOCK = LOCK.writeLock(); + private final JBLock WRITE_LOCK = LOCK.writeLock(); private final List myRootsToWatch = new ArrayList(); private WatchRequest[] myCachedNormalizedRequests = null; private final FileWatcher myWatcher; - private final LocalFileSystemBase myNativeFileSystem; - private static class WatchRequestImpl implements WatchRequest { public final String myRootPath; @@ -132,7 +131,6 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap if (myWatcher.isOperational()) { new StoreRefreshStatusThread().start(); } - myNativeFileSystem = null; } @Override @@ -471,12 +469,6 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap return false; } - @NonNls - public String toString() { - return "LocalFileSystem"; - } - - @Override public void refreshWithoutFileWatcher(final boolean asynchronous) { Runnable heavyRefresh = new Runnable() { @@ -499,33 +491,12 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap } @Override - public boolean exists(@NotNull final VirtualFile fileOrDirectory) { - if (myNativeFileSystem == null) return super.exists(fileOrDirectory); - else return myNativeFileSystem.exists(fileOrDirectory); + public FileAttributes getAttributes(@NotNull final VirtualFile file) { + return FileSystemUtil.getAttributes(FileUtil.toSystemDependentName(file.getPath())); } - @Override - public long getTimeStamp(@NotNull final VirtualFile file) { - if (myNativeFileSystem == null) return super.getTimeStamp(file); - else return myNativeFileSystem.getTimeStamp(file); - } - - @Override - public boolean isDirectory(@NotNull final VirtualFile file) { - if (myNativeFileSystem == null) return super.isDirectory(file); - else return myNativeFileSystem.isDirectory(file); - } - - @Override - public boolean isWritable(@NotNull final VirtualFile file) { - if (myNativeFileSystem == null) return super.isWritable(file); - else return myNativeFileSystem.isWritable(file); - } - - @Override - @NotNull - public String[] list(@NotNull final VirtualFile file) { - if (myNativeFileSystem == null) return super.list(file); - else return myNativeFileSystem.list(file); + @NonNls + public String toString() { + return "LocalFileSystem"; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32Kernel.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32Kernel.java index 501b706420db..f99169a8d569 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32Kernel.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32Kernel.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. @@ -97,7 +97,7 @@ public class Win32Kernel { } @Nullable - private FileInfo doGetInfo(String path) { + FileInfo doGetInfo(String path) { FileInfo info = myCache.get(path); if (info == null) { info = myKernel.getInfo(path.replace('/', '\\')); 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 81e6f06cf1c4..3c3a000631e7 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 @@ -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. @@ -18,6 +18,8 @@ package com.intellij.openapi.vfs.impl.win32; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; +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.impl.local.LocalFileSystemBase; import com.intellij.util.ArrayUtil; @@ -202,4 +204,20 @@ public class Win32LocalFileSystem extends LocalFileSystemBase { public int getBooleanAttributes(@NotNull VirtualFile file, int flags) { return myKernel.getBooleanAttributes(file.getPath(), flags); } + + @Override + public FileAttributes getAttributes(@NotNull final VirtualFile file) { + final FileInfo fileInfo = myKernel.doGetInfo(FileUtil.toSystemDependentName(file.getPath())); + if (fileInfo == null) return null; + + final boolean isDirectory = isSet(fileInfo, Win32Kernel.FILE_ATTRIBUTE_DIRECTORY); + final boolean isSymlink = isSet(fileInfo, Win32Kernel.FILE_ATTRIBUTE_REPARSE_POINT); + final boolean isSpecial = isSet(fileInfo, Win32Kernel.FILE_ATTRIBUTE_DEVICE); + final boolean isWritable = !isSet(fileInfo, Win32Kernel.FILE_ATTRIBUTE_READONLY); + return new FileAttributes(isDirectory, isSymlink, isSpecial, fileInfo.length, fileInfo.timestamp, isWritable); + } + + private static boolean isSet(final FileInfo fileInfo, final int bit) { + return (fileInfo.attributes & bit) == bit; + } } 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 69a5848c2428..39bf2e404f88 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 @@ -499,10 +499,10 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile { } @Override - public VirtualFile getCanonicalFile() { + public NewVirtualFile getCanonicalFile() { if (getFlagInt(HAS_SYMLINK_FLAG)) { final String path = getCanonicalPath(); - return path != null ? getFileSystem().findFileByPath(path) : null; + return path != null ? (NewVirtualFile)getFileSystem().findFileByPath(path) : null; } return this; } diff --git a/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java b/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java index 59be016ec53c..14db11d1cffc 100644 --- a/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java +++ b/platform/testFramework/src/com/intellij/mock/MockLocalFileSystem.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -16,6 +16,7 @@ package com.intellij.mock; +import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileOperationsHandler; import com.intellij.openapi.vfs.LocalFileSystem; @@ -256,4 +257,9 @@ public class MockLocalFileSystem extends LocalFileSystem { 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 new file mode 100644 index 000000000000..24b6a1001fb2 --- /dev/null +++ b/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java @@ -0,0 +1,138 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.util.io; + +import com.intellij.util.StringBuilderSpinAllocator; +import org.intellij.lang.annotations.MagicConstant; + +/** + * @version 11.1 + * @see FileSystemUtil#getAttributes(String) + */ +@SuppressWarnings("OctalInteger") +public final class FileAttributes { + public static final int OWNER_READ = 0400; + public static final int OWNER_WRITE = 0200; + public static final int OWNER_EXECUTE = 0100; + public static final int GROUP_READ = 0040; + public static final int GROUP_WRITE = 0020; + public static final int GROUP_EXECUTE = 0010; + public static final int OTHERS_READ = 0004; + public static final int OTHERS_WRITE = 0002; + public static final int OTHERS_EXECUTE = 0001; + + @MagicConstant(flags = { + OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE + }) + public @interface Permissions { } + + public final boolean isFile; + public final boolean isDirectory; + public final boolean isSymlink; + public final boolean isSpecial; + + /** In bytes, 0 for symlinks and special files. */ + public final long length; + + /** In milliseconds (note that actual resolution may be less accurate). */ + public final long lastModified; + + /** UNIX permission bits (for Windows only OWNER_WRITE matters and OWNER_READ/EXECUTE are always set), or -1 if not supported. */ + @Permissions + public final int permissions; + + // todo: hidden flag (?) + + public FileAttributes(final boolean isDirectory, + final boolean isSymlink, + final boolean isSpecial, + final long length, + final long lastModified, + final boolean writable) { + this(isDirectory, isSymlink, isSpecial, length, lastModified, OWNER_READ | OWNER_EXECUTE | (writable ? OWNER_WRITE : 0)); + } + + public FileAttributes(final boolean isDirectory, + final boolean isSymlink, + final boolean isSpecial, + final long length, + final long lastModified, + @Permissions final int permissions) { + this.isFile = !isDirectory && !isSymlink && !isSpecial; + this.isDirectory = isDirectory; + this.isSymlink = isSymlink; + this.isSpecial = isSpecial; + this.length = isSymlink || isSpecial ? 0 : length; + this.lastModified = lastModified; + this.permissions = permissions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final FileAttributes that = (FileAttributes)o; + + if (isDirectory != that.isDirectory) return false; + if (isFile != that.isFile) return false; + if (isSpecial != that.isSpecial) return false; + if (isSymlink != that.isSymlink) return false; + if (lastModified != that.lastModified) return false; + if (permissions != that.permissions) return false; + if (length != that.length) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (isFile ? 1 : 0); + result = 31 * result + (isDirectory ? 1 : 0); + result = 31 * result + (isSymlink ? 1 : 0); + result = 31 * result + (isSpecial ? 1 : 0); + result = 31 * result + (int)(length ^ (length >>> 32)); + result = 31 * result + (int)(lastModified ^ (lastModified >>> 32)); + result = 31 * result + permissions; + return result; + } + + @Override + public String toString() { + final StringBuilder sb = StringBuilderSpinAllocator.alloc(); + try { + sb.append("[type:"); + if (isFile) sb.append('f'); + if (isDirectory) sb.append('d'); + if (isSymlink) sb.append('l'); + if (isSpecial) sb.append('!'); + + sb.append(" length:").append(length); + + sb.append(" modified:").append(lastModified); + + if (permissions != -1) { + sb.append(" mode:").append(Integer.toOctalString(permissions)); + } + + sb.append(']'); + return sb.toString(); + } + finally { + StringBuilderSpinAllocator.dispose(sb); + } + } +} 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 cd2d7186e619..8cd1b9cbed6f 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java @@ -27,61 +27,69 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; -// todo[r.sh] read attributes in one shot (IDEA 12) -// todo[r.sh] use NIO2 API after migration to JDK 7 +/** + * @version 11.1 + */ public class FileSystemUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.io.FileSystemUtil"); - @Nullable + @NotNull private static final Mediator ourMediator; - static { Mediator mediator = null; - if (SystemInfo.areSymLinksSupported) { - if (SystemInfo.isJavaVersionAtLeast("1.7")) { - try { - mediator = new Jdk7MediatorImpl(); - mediator.isSymLink("/"); // quick test - } - catch (Throwable t) { - if ("1.7.0-ea".equals(SystemInfo.JAVA_VERSION)) { - LOG.warn(t); - } - else { - LOG.error(t); - } - mediator = null; - } + + // todo: move IdeaWin32 interface to this package, add mediator + + if (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD) { + try { + mediator = new JnaUnixMediatorImpl(); + mediator.getAttributes("/"); // quick test } - if (mediator == null) { - if (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD) { - try { - mediator = new JnaUnixMediatorImpl(); - mediator.isSymLink("/"); // quick test - } - catch (Throwable t) { - LOG.error(t); - mediator = null; - } - } + catch (Throwable t) { + LOG.error(t); + mediator = null; } } + + if (mediator == null && SystemInfo.isJavaVersionAtLeast("1.7") && !"1.7.0-ea".equals(SystemInfo.JAVA_VERSION)) { + try { + mediator = new Jdk7MediatorImpl(); + mediator.getAttributes("/"); // quick test + } + catch (Throwable t) { + LOG.error(t); + mediator = null; + } + } + + if (mediator == null) { + // todo: after introducing IdeaWin32 mediator, fail tests at this point, or issue a warning in production + mediator = new StandardMediatorImpl(); + } + ourMediator = mediator; } private FileSystemUtil() { } - public static boolean isSymLink(@NotNull final String path) { + @Nullable + public static FileAttributes getAttributes(@NotNull final String path) { try { - return ourMediator != null && ourMediator.isSymLink(path); + return ourMediator.getAttributes(path); } catch (Exception e) { LOG.warn(e); - return false; } + return null; + } + + public static boolean isSymLink(@NotNull final String path) { + final FileAttributes attributes = getAttributes(path); + return attributes != null && attributes.isSymlink; } public static boolean isSymLink(@NotNull final File file) { @@ -90,17 +98,15 @@ public class FileSystemUtil { @Nullable public static String resolveSymLink(@NotNull final String path) { - if (ourMediator != null) { - try { - final String realPath = ourMediator.resolveSymLink(path); - if (realPath != null && new File(realPath).exists()) { - return realPath; - } - } - catch (Exception e) { - LOG.warn(e); + try { + final String realPath = ourMediator.resolveSymLink(path); + if (realPath != null && new File(realPath).exists()) { + return realPath; } } + catch (Exception e) { + LOG.warn(e); + } return null; } @@ -110,15 +116,8 @@ public class FileSystemUtil { } public static int getPermissions(@NotNull final String path) { - if (SystemInfo.isUnix && ourMediator != null) { - try { - return ourMediator.getPermissions(path); - } - catch (Exception e) { - LOG.warn(e); - } - } - return -1; + final FileAttributes attributes = getAttributes(path); + return attributes != null ? attributes.permissions : -1; } public static int getPermissions(@NotNull final File file) { @@ -126,7 +125,7 @@ public class FileSystemUtil { } public static void setPermissions(@NotNull final String path, final int permissions) { - if (SystemInfo.isUnix && ourMediator != null) { + if (SystemInfo.isUnix) { try { ourMediator.setPermissions(path, permissions); } @@ -141,16 +140,16 @@ public class FileSystemUtil { } private interface Mediator { - boolean isSymLink(@NotNull String path) throws Exception; + @Nullable + FileAttributes getAttributes(@NotNull String path) throws Exception; @Nullable String resolveSymLink(@NotNull String path) throws Exception; - int getPermissions(@NotNull String path) throws Exception; - void setPermissions(@NotNull String path, int permissions) throws Exception; } + // todo[r.sh] remove reflection after migration to JDK 7 @SuppressWarnings("OctalInteger") private static class Jdk7MediatorImpl implements Mediator { private final Object myDefaultFileSystem; @@ -158,8 +157,9 @@ public class FileSystemUtil { private final Method myIsSymbolicLink; private final Object myLinkOptions; private final Object myNoFollowLinkOptions; - private final Method myGetAttribute; + private final Method myReadAttributes; private final Method mySetAttribute; + private final Method myToMillis; private Jdk7MediatorImpl() throws Exception { myDefaultFileSystem = Class.forName("java.nio.file.FileSystems").getMethod("getDefault").invoke(null); @@ -168,7 +168,8 @@ public class FileSystemUtil { myGetPath.setAccessible(true); final Class pathClass = Class.forName("java.nio.file.Path"); - myIsSymbolicLink = Class.forName("java.nio.file.Files").getMethod("isSymbolicLink", pathClass); + final Class filesClass = Class.forName("java.nio.file.Files"); + myIsSymbolicLink = filesClass.getMethod("isSymbolicLink", pathClass); myIsSymbolicLink.setAccessible(true); final Class linkOptClass = Class.forName("java.nio.file.LinkOption"); @@ -177,14 +178,35 @@ public class FileSystemUtil { Array.set(myNoFollowLinkOptions, 0, linkOptClass.getField("NOFOLLOW_LINKS").get(null)); final Class linkOptArrClass = myLinkOptions.getClass(); - myGetAttribute = Class.forName("java.nio.file.Files").getMethod("getAttribute", pathClass, String.class, linkOptArrClass); - mySetAttribute = Class.forName("java.nio.file.Files").getMethod("setAttribute", pathClass, String.class, Object.class, linkOptArrClass); + myReadAttributes = filesClass.getMethod("readAttributes", pathClass, String.class, linkOptArrClass); + myReadAttributes.setAccessible(true); + mySetAttribute = filesClass.getMethod("setAttribute", pathClass, String.class, Object.class, linkOptArrClass); + mySetAttribute.setAccessible(true); + + final Class fileTimeClass = Class.forName("java.nio.file.attribute.FileTime"); + myToMillis = fileTimeClass.getMethod("toMillis"); + myToMillis.setAccessible(true); } @Override - public boolean isSymLink(@NotNull final String path) throws Exception { + public FileAttributes getAttributes(@NotNull final String path) throws Exception { final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY); - return (Boolean)myIsSymbolicLink.invoke(null, pathObj); + try { + final Map attributes = (Map)myReadAttributes.invoke(null, pathObj, "posix:permissions,*", myNoFollowLinkOptions); + return new FileAttributes((Boolean)attributes.get("isDirectory"), + (Boolean)attributes.get("isSymbolicLink"), + (Boolean)attributes.get("isOther"), + (Long)attributes.get("size"), + (Long)myToMillis.invoke(attributes.get("lastModifiedTime")), + decodePermissions(attributes.get("permissions"))); + } + catch (InvocationTargetException e) { + final Throwable cause = e.getCause(); + if (cause != null && "java.nio.file.NoSuchFileException".equals(cause.getClass().getName())) { + return null; + } + throw e; + } } @Override @@ -196,38 +218,30 @@ public class FileSystemUtil { return toRealPath.invoke(pathObj, myLinkOptions).toString(); } - @Override - public int getPermissions(@NotNull final String path) throws Exception { - final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY); - final Object attribute = myGetAttribute.invoke(null, pathObj, POSIX_PERMISSIONS_ATTR, myNoFollowLinkOptions); - return decodePermissions(attribute); - } - @Override public void setPermissions(@NotNull final String path, final int permissions) throws Exception { final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY); final Object attribute = encodePermissions(permissions); if (attribute != null) { - mySetAttribute.invoke(null, pathObj, POSIX_PERMISSIONS_ATTR, attribute, myLinkOptions); + mySetAttribute.invoke(null, pathObj, "posix:permissions", attribute, myLinkOptions); } } - private static final String POSIX_PERMISSIONS_ATTR = "posix:permissions"; - private static final Map ATTRIBUTES_MAP; static { ATTRIBUTES_MAP = new HashMap(); - ATTRIBUTES_MAP.put("OWNER_READ", 0400); - ATTRIBUTES_MAP.put("OWNER_WRITE", 0200); - ATTRIBUTES_MAP.put("OWNER_EXECUTE", 0100); - ATTRIBUTES_MAP.put("GROUP_READ", 0040); - ATTRIBUTES_MAP.put("GROUP_WRITE", 0020); - ATTRIBUTES_MAP.put("GROUP_EXECUTE", 0010); - ATTRIBUTES_MAP.put("OTHERS_READ", 0004); - ATTRIBUTES_MAP.put("OTHERS_WRITE", 0002); - ATTRIBUTES_MAP.put("OTHERS_EXECUTE", 0001); + ATTRIBUTES_MAP.put("OWNER_READ", FileAttributes.OWNER_READ); + ATTRIBUTES_MAP.put("OWNER_WRITE", FileAttributes.OWNER_WRITE); + ATTRIBUTES_MAP.put("OWNER_EXECUTE", FileAttributes.OWNER_EXECUTE); + ATTRIBUTES_MAP.put("GROUP_READ", FileAttributes.GROUP_READ); + ATTRIBUTES_MAP.put("GROUP_WRITE", FileAttributes.GROUP_WRITE); + ATTRIBUTES_MAP.put("GROUP_EXECUTE", FileAttributes.GROUP_EXECUTE); + ATTRIBUTES_MAP.put("OTHERS_READ", FileAttributes.OTHERS_READ); + ATTRIBUTES_MAP.put("OTHERS_WRITE", FileAttributes.OTHERS_WRITE); + ATTRIBUTES_MAP.put("OTHERS_EXECUTE", FileAttributes.OTHERS_EXECUTE); } + @FileAttributes.Permissions private static int decodePermissions(final Object o) { if (!(o instanceof Collection)) return -1; @@ -236,6 +250,7 @@ public class FileSystemUtil { final Integer bit = ATTRIBUTES_MAP.get(attr.toString()); if (bit != null) value |= bit; } + //noinspection MagicConstant return value; } @@ -260,11 +275,14 @@ public class FileSystemUtil { } // thanks to SVNKit for the idea - @SuppressWarnings("OctalInteger") private static class JnaUnixMediatorImpl implements Mediator { + @SuppressWarnings("OctalInteger") private interface LibC extends Library { + // from stat(2) int S_MASK = 0177777; - int S_IFLNK = 0120000; + int S_IFLNK = 0120000; // symbolic link + int S_IFREG = 0100000; // regular file + int S_IFDIR = 0040000; // directory int PERM_MASK = 0777; int lstat(String path, Pointer stat); @@ -274,34 +292,45 @@ public class FileSystemUtil { private final LibC myLibC; private final Memory mySharedMem; - private final int myOffset; + private final int myModeOffset; + private final int mySizeOffset; + private final int myTimeOffset; private JnaUnixMediatorImpl() throws Exception { myLibC = (LibC)Native.loadLibrary("c", LibC.class); mySharedMem = new Memory(512); - myOffset = SystemInfo.isLinux ? (SystemInfo.is32Bit ? 16 : 24) : - SystemInfo.isMac | SystemInfo.isFreeBSD ? 8 : - SystemInfo.isSolaris ? (SystemInfo.is32Bit ? 20 : 16) : - -1; - if (myOffset < 0) throw new IllegalStateException("Unsupported OS: " + SystemInfo.OS_NAME); - } - - private synchronized int getMode(final String path) { - mySharedMem.clear(); - final int res = SystemInfo.isLinux ? myLibC.__lxstat64(0, path, mySharedMem) : myLibC.lstat(path, mySharedMem); - if (res == 0) { - return (SystemInfo.isLinux ? mySharedMem.getInt(myOffset) : mySharedMem.getShort(myOffset)) & LibC.S_MASK; - } - else { - LOG.debug("lstat(" + path + "): " + res); - return -1; - } + myModeOffset = SystemInfo.isLinux ? (SystemInfo.is32Bit ? 16 : 24) : + SystemInfo.isMac | SystemInfo.isFreeBSD ? 8 : + SystemInfo.isSolaris ? (SystemInfo.is32Bit ? 20 : 16) : + -1; + mySizeOffset = SystemInfo.isLinux ? (SystemInfo.is32Bit ? 44 : 48) : + SystemInfo.isMac | SystemInfo.isFreeBSD ? (SystemInfo.is32Bit ? 48 : 72) : + SystemInfo.isSolaris ? (SystemInfo.is32Bit ? 48 : 40) : + -1; + myTimeOffset = SystemInfo.isLinux ? (SystemInfo.is32Bit ? 72 : 88) : + SystemInfo.isMac | SystemInfo.isFreeBSD ? 40 : + SystemInfo.isSolaris ? 64 : + -1; + if (myModeOffset < 0) throw new IllegalStateException("Unsupported OS: " + SystemInfo.OS_NAME); } @Override - public boolean isSymLink(@NotNull final String path) throws Exception { - final int mode = getMode(path); - return mode != -1 && (mode & LibC.S_IFLNK) == LibC.S_IFLNK; + public synchronized FileAttributes getAttributes(@NotNull final String path) throws Exception { + mySharedMem.clear(); + final int res = SystemInfo.isLinux ? myLibC.__lxstat64(0, path, mySharedMem) : myLibC.lstat(path, mySharedMem); + if (res == 0) { + final int mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK; + final boolean isDirectory = (mode & LibC.S_IFDIR) == LibC.S_IFDIR; + final boolean isSymlink = (mode & LibC.S_IFLNK) == LibC.S_IFLNK; + final boolean isSpecial = !isDirectory && !isSymlink && (mode & LibC.S_IFREG) == 0; + final long size = mySharedMem.getLong(mySizeOffset); + final long mTime1 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset) : mySharedMem.getLong(myTimeOffset); + final long mTime2 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset + 4) : mySharedMem.getLong(myTimeOffset + 8); + final long mTime = mTime1 * 1000 + mTime2 / 1000000; + @FileAttributes.Permissions final int permissions = mode & LibC.PERM_MASK; + return new FileAttributes(isDirectory, isSymlink, isSpecial, size, mTime, permissions); + } + return null; } @Override @@ -309,15 +338,68 @@ public class FileSystemUtil { return new File(path).getCanonicalPath(); } - @Override - public int getPermissions(@NotNull final String path) throws Exception { - final int mode = getMode(path); - return mode != -1 ? mode & LibC.PERM_MASK : -1; - } - @Override public void setPermissions(@NotNull final String path, final int permissions) throws Exception { myLibC.chmod(path, permissions & LibC.PERM_MASK); } } + + private static class StandardMediatorImpl implements Mediator { + // from java.io.FileSystem + private static final int BA_REGULAR = 0x02; + private static final int BA_DIRECTORY = 0x04; + + private final Object myFileSystem; + private final Method myGetBooleanAttributes; + + private StandardMediatorImpl() { + Object fileSystem; + Method getBooleanAttributes; + try { + final Class fsClass = Class.forName("java.io.FileSystem"); + final Method getFileSystem = fsClass.getMethod("getFileSystem"); + getFileSystem.setAccessible(true); + fileSystem = getFileSystem.invoke(null); + getBooleanAttributes = fsClass.getDeclaredMethod("getBooleanAttributes", File.class); + getBooleanAttributes.setAccessible(true); + } + catch (Throwable t) { + fileSystem = null; + getBooleanAttributes = null; + } + myFileSystem = fileSystem; + myGetBooleanAttributes = getBooleanAttributes; + } + + @Override + public FileAttributes getAttributes(@NotNull final String path) throws Exception { + final File file = new File(path); + if (myFileSystem != null) { + final int flags = (Integer)myGetBooleanAttributes.invoke(myFileSystem, file); + if (flags != 0) { + final boolean isDirectory = (flags & BA_DIRECTORY) != 0; + final boolean isSpecial = (flags & (BA_REGULAR | BA_DIRECTORY)) == 0; + return new FileAttributes(isDirectory, false, isSpecial, file.length(), file.lastModified(), file.canWrite()); + } + } + else { + if (file.exists()) { + final boolean isDirectory = file.isDirectory(); + final boolean isSpecial = !isDirectory && !file.isFile(); + return new FileAttributes(isDirectory, false, isSpecial, file.length(), file.lastModified(), file.canWrite()); + } + } + + return null; + } + + @Override + public String resolveSymLink(@NotNull final String path) throws Exception { + return new File(path).getCanonicalPath(); + } + + @Override + public void setPermissions(@NotNull final String path, final int permissions) throws Exception { + } + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAEngine.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAEngine.java index 020193a1cd44..72e975a3b555 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAEngine.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAEngine.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.groovy.lang.psi.dataFlow; +import com.intellij.openapi.progress.ProgressManager; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.CallEnvironment; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.CallInstruction; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ControlFlowBuilderUtil; @@ -77,6 +78,7 @@ public class DFAEngine { visited[instr.num()] = true; while (!workList.isEmpty()) { + ProgressManager.checkCanceled(); final Instruction curr = workList.remove(); final int num = curr.num(); final E oldE = info.get(num);