From 644286b86f736bee6837803276c5679e29329a2d Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 15 Mar 2012 17:58:02 +0100 Subject: [PATCH] IDEA-82554 (find file by symlink traversal path) --- .../com/intellij/openapi/vfs/VirtualFile.java | 8 +++- .../openapi/vfs/newvfs/VfsImplUtil.java | 47 +++++++++++++------ .../vfs/local/SymlinkHandlingTest.java | 20 ++++++++ 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java index e44e3f98874f..21ea1e06ff67 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java @@ -341,7 +341,13 @@ public abstract class VirtualFile extends UserDataHolderBase implements Modifica child = this; } else if (name.equals("..")) { - child = getParent(); + if (isSymLink()) { + final VirtualFile canonicalFile = getCanonicalFile(); + child = canonicalFile != null ? canonicalFile.getParent() : null; + } + else { + child = getParent(); + } } else { child = findChild(name); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java index 68b669f11cf3..1b0e8a758a18 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.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. @@ -17,7 +17,6 @@ package com.intellij.openapi.vfs.newvfs; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,29 +24,31 @@ import org.jetbrains.annotations.Nullable; import java.io.File; public class VfsImplUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.VfsImplUtil"); @NonNls private static final String FILE_SEPARATORS = "/" + File.separator; - private VfsImplUtil() { - } + private VfsImplUtil() { } @Nullable - public static VirtualFile findFileByPath(NewVirtualFileSystem vfs, @NotNull @NonNls final String path) { + public static NewVirtualFile findFileByPath(@NotNull final NewVirtualFileSystem vfs, @NotNull @NonNls final String path) { final String normalizedPath = vfs.normalize(path); if (normalizedPath == null) return null; final String basePath = vfs.extractRootPath(normalizedPath); NewVirtualFile file = ManagingFS.getInstance().findRoot(basePath, vfs); if (file == null || !file.exists()) return null; + if (normalizedPath.length() < basePath.length()) return null; - if (normalizedPath.length() < basePath.length()) { - return null; - } for (String pathElement : StringUtil.tokenize(normalizedPath.substring(basePath.length()), FILE_SEPARATORS)) { if (pathElement.isEmpty() || ".".equals(pathElement)) continue; if ("..".equals(pathElement)) { - file = file.getParent(); + if (file.isSymLink()) { + final NewVirtualFile canonicalFile = file.getCanonicalFile(); + file = canonicalFile != null ? canonicalFile.getParent() : null; + } + else { + file = file.getParent(); + } } else { file = file.findChild(pathElement); @@ -60,7 +61,7 @@ public class VfsImplUtil { } @Nullable - public static VirtualFile findFileByPathIfCached(NewVirtualFileSystem vfs, @NotNull @NonNls String path) { + public static NewVirtualFile findFileByPathIfCached(@NotNull final NewVirtualFileSystem vfs, @NotNull @NonNls final String path) { final String normalizedPath = vfs.normalize(path); if (normalizedPath == null) return null; final String basePath = vfs.extractRootPath(normalizedPath); @@ -70,7 +71,14 @@ public class VfsImplUtil { for (String pathElement : StringUtil.tokenize(normalizedPath.substring(basePath.length()), FILE_SEPARATORS)) { if (pathElement.isEmpty() || ".".equals(pathElement)) continue; if ("..".equals(pathElement)) { - file = file.getParent(); + if (file.isSymLink()) { + final String canonicalPath = file.getCanonicalPath(); + final NewVirtualFile canonicalFile = canonicalPath != null ? findFileByPathIfCached(vfs, canonicalPath) : null; + file = canonicalFile != null ? canonicalFile.getParent() : null; + } + else { + file = file.getParent(); + } } else { file = file.findChildIfCached(pathElement); @@ -83,18 +91,27 @@ public class VfsImplUtil { } @Nullable - public static VirtualFile refreshAndFindFileByPath(NewVirtualFileSystem vfs, @NotNull final String path) { + public static NewVirtualFile refreshAndFindFileByPath(@NotNull final NewVirtualFileSystem vfs, @NotNull @NonNls final String path) { final String normalizedPath = vfs.normalize(path); if (normalizedPath == null) return null; final String basePath = vfs.extractRootPath(normalizedPath); NewVirtualFile file = ManagingFS.getInstance().findRoot(basePath, vfs); if (file == null || !file.exists()) return null; - LOG.assertTrue(basePath.length() <= normalizedPath.length(), vfs + " failed to extract root path: " + basePath + " from " + normalizedPath); + LOG.assertTrue(basePath.length() <= normalizedPath.length(), + vfs + " failed to extract root path '" + basePath + "' from '" + normalizedPath + "'"); + for (String pathElement : StringUtil.tokenize(normalizedPath.substring(basePath.length()), FILE_SEPARATORS)) { if (pathElement.isEmpty() || ".".equals(pathElement)) continue; if ("..".equals(pathElement)) { - file = file.getParent(); + if (file.isSymLink()) { + final String canonicalPath = file.getCanonicalPath(); + final NewVirtualFile canonicalFile = canonicalPath != null ? refreshAndFindFileByPath(vfs, canonicalPath) : null; + file = canonicalFile != null ? canonicalFile.getParent() : null; + } + else { + file = file.getParent(); + } } else { file = file.refreshAndFindChild(pathElement); 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 8c2073887b9d..025a888e1a20 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 @@ -245,6 +245,26 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase { assertEquals(linkContent, fileContent); } + public void testFindByLinkParentPath() throws Exception { + final File topDir = FileUtil.createTempDirectory("topDir.", null); + final File subDir1 = FileUtil.createTempDirectory(topDir, "subDir1.", null); + final File link = createTempLink(subDir1.getAbsolutePath(), "link"); + final VirtualFile vLink = refreshAndFind(link); + assertNotNull(link.getPath(), vLink); + + final File subDir2 = FileUtil.createTempDirectory(topDir, "subDir2.", null); + final File subChild = FileUtil.createTempFile(subDir2, "subChild.", ".txt", true); + final VirtualFile vSubChild = refreshAndFind(subChild); + assertNotNull(subChild.getPath(), vSubChild); + + final String relPath = "../" + subDir2.getName() + "/" + subChild.getName(); + VirtualFile vSubChildRel; + vSubChildRel = vLink.findFileByRelativePath(relPath); + assertEquals(vSubChild, vSubChildRel); + vSubChildRel = LocalFileSystem.getInstance().findFileByPath(vLink.getPath() + "/" + relPath); + assertEquals(vSubChild, vSubChildRel); + } + // todo[r.sh] use NIO2 API after migration to JDK 7 private static File createTempLink(final String target, final String link) throws InterruptedException, ExecutionException { final boolean isAbsolute = SystemInfo.isUnix && StringUtil.startsWithChar(link, '/') ||