diff --git a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileVisitor.java b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileVisitor.java index c092d1e6e197..37bf1452ff4a 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileVisitor.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileVisitor.java @@ -21,7 +21,8 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Set; +import java.util.List; +import java.util.Map; /** * @author Dmitry Avdeev @@ -84,7 +85,7 @@ public abstract class VirtualFileVisitor { private boolean mySkipRoot = false; private int myDepthLimit = -1; - private Set myVisitedTargets; + private Map> myVisitedTargets; private int myLevel = 0; private Stack myValueStack = null; private T myValue = null; @@ -102,7 +103,7 @@ public abstract class VirtualFileVisitor { } } if (myFollowSymLinks) { - myVisitedTargets = ContainerUtil.newHashSet(); + myVisitedTargets = ContainerUtil.newHashMap(); } } @@ -184,11 +185,21 @@ public abstract class VirtualFileVisitor { } VirtualFile target = file.getCanonicalFile(); - if (!myVisitedTargets.add(target)) { - return false; + List links = myVisitedTargets.get(target); + if (links == null) { + myVisitedTargets.put(target, ContainerUtil.newSmartList(file)); + return true; } - return true; + boolean hasLoop = false; + for (VirtualFile link : links) { + if (VfsUtilCore.isAncestor(link, file, true)) { + hasLoop = true; + break; + } + } + links.add(file); + return !hasLoop; } final boolean depthLimitReached() { 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 3710fb4bed88..218cc4c865d6 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -116,6 +116,16 @@ public class SymlinkHandlingTest extends SymlinkTestCase { link2.getPath() + "/" + link1Home.getName() + "/" + link1.getName()); } + public void testVisitAllNonRecursiveLinks() throws Exception { + File target = createTestDir(myTempDir, "target"); + File child = createTestDir(target, "child"); + File link1 = createSymLink(target.getPath(), myTempDir.getPath() + "/link1"); + File link2 = createSymLink(target.getPath(), myTempDir.getPath() + "/link2"); + assertVisitedPaths(target.getPath(), child.getPath(), + link1.getPath(), link1.getPath() + "/child", + link2.getPath(), link2.getPath() + "/child"); + } + public void testTargetIsWritable() throws Exception { File targetFile = createTestFile(myTempDir, "target.txt"); File linkFile = createSymLink(targetFile.getPath(), myTempDir + "/link"); @@ -366,14 +376,13 @@ public class SymlinkHandlingTest extends SymlinkTestCase { VirtualFile vDir = refreshAndFind(from); assertNotNull(vDir); - Set expectedSet = new HashSet(expected.length + 1, 1); - ContainerUtil.addAll(expectedSet, vDir.getPath()); - ContainerUtil.addAll(expectedSet, ContainerUtil.map(expected, new Function() { + Set expectedSet = ContainerUtil.map2Set(expected, new Function() { @Override public String fun(String path) { return FileUtil.toSystemIndependentName(path); } - })); + }); + expectedSet.add(vDir.getPath()); final Set actualSet = new HashSet(); VfsUtilCore.visitChildrenRecursively(vDir, new VirtualFileVisitor() {