VFS visitor extended

This commit is contained in:
Roman Shevchenko
2012-08-24 22:19:11 +04:00
parent eefc0d3462
commit 7c310d870e
11 changed files with 628 additions and 102 deletions
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.vfs;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Function;
@@ -26,9 +27,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class VfsUtilCore {
/**
@@ -170,38 +169,63 @@ public class VfsUtilCore {
return stream;
}
public static void visitChildrenRecursively(@NotNull VirtualFile file, @NotNull VirtualFileVisitor visitor) {
visitChildrenRecursively(file, visitor, null);
public static boolean iterateChildrenRecursively(@NotNull final VirtualFile root,
@Nullable final VirtualFileFilter filter,
@NotNull final ContentIterator iterator) {
final VirtualFileVisitor.Result result = visitChildrenRecursively(root, new VirtualFileVisitor() {
@NotNull
@Override
public Result visitFileEx(@NotNull VirtualFile file) {
if (filter != null && !filter.accept(file)) return SKIP_CHILDREN;
if (!iterator.processFile(file)) return skipTo(root);
return CONTINUE;
}
});
return result.skipToParent != root;
}
private static void visitChildrenRecursively(@NotNull VirtualFile file,
@NotNull VirtualFileVisitor visitor,
@Nullable Set<VirtualFile> visitedSymLinks) {
if (!file.isValid()) return;
if (!visitor.visitFile(file)) return;
public static VirtualFileVisitor.Result visitChildrenRecursively(@NotNull VirtualFile file, @NotNull VirtualFileVisitor visitor) {
if (!file.isValid()) return VirtualFileVisitor.CONTINUE;
boolean visitChildren = true;
if (file.isSymLink()) {
if (!visitor.followSymLinks()) return;
if (visitedSymLinks == null) {
visitedSymLinks = new HashSet<VirtualFile>();
visitor.pushFrame();
try {
final boolean visited = visitor.allowVisitFile(file);
if (visited) {
VirtualFileVisitor.Result result = visitor.visitFileEx(file);
if (result.skipChildren) return result;
}
if (!visitedSymLinks.add(file) || isInvalidLink(file)) {
visitChildren = false;
if (!visitor.allowVisitChildren(file)) return VirtualFileVisitor.CONTINUE;
if (!visitor.depthLimitReached()) {
final Iterable<VirtualFile> iterable = visitor.getChildrenIterable(file);
if (iterable != null) {
for (VirtualFile child : iterable) {
VirtualFileVisitor.Result result = visitChildrenRecursively(child, visitor);
if (result.skipToParent != null && result.skipToParent != child) return result;
}
}
else {
@SuppressWarnings("UnsafeVfsRecursion") VirtualFile[] children = file.getChildren();
for (VirtualFile child : children) {
VirtualFileVisitor.Result result = visitChildrenRecursively(child, visitor);
if (result.skipToParent != null && result.skipToParent != child) return result;
}
}
}
if (visited) {
visitor.afterChildrenVisited(file);
}
return VirtualFileVisitor.CONTINUE;
}
if (visitChildren) {
@SuppressWarnings("UnsafeVfsRecursion") VirtualFile[] children = file.getChildren();
for (VirtualFile child : children) {
visitChildrenRecursively(child, visitor, visitedSymLinks);
}
finally {
visitor.popFrame();
}
visitor.afterChildrenVisited(file);
}
private static boolean isInvalidLink(@NotNull VirtualFile link) {
public static boolean isInvalidLink(@NotNull VirtualFile link) {
final VirtualFile target = link.getCanonicalFile();
return target == null || target == link || isAncestor(target, link, true);
}
@@ -15,28 +15,182 @@
*/
package com.intellij.openapi.vfs;
import com.intellij.openapi.util.Key;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Dmitry Avdeev
* @since 31.10.2011
*/
public abstract class VirtualFileVisitor {
private final boolean myFollowSymLinks;
public static class Option {
private Option() { }
protected VirtualFileVisitor() {
this(true);
private static class LimitOption extends Option {
private final int limit;
private LimitOption(int limit) {
this.limit = limit;
}
}
}
protected VirtualFileVisitor(boolean followSymLinks) {
myFollowSymLinks = followSymLinks;
public static final Option NO_FOLLOW_SYMLINKS = new Option();
public static final Option SKIP_ROOT = new Option();
public static final Option ONE_LEVEL_DEEP = new Option.LimitOption(1);
public static Option limit(int maxDepth) {
return new Option.LimitOption(maxDepth);
}
public boolean followSymLinks() {
return myFollowSymLinks;
public static class Result {
public final boolean skipChildren;
public final VirtualFile skipToParent;
private Result(boolean skipChildren, @Nullable VirtualFile skipToParent) {
this.skipChildren = skipChildren;
this.skipToParent = skipToParent;
}
@Override
public String toString() {
return "(" + (skipChildren ? "skip," + skipToParent : "continue") + ")";
}
}
public abstract boolean visitFile(@NotNull VirtualFile file);
public static final Result CONTINUE = new Result(false, null);
public static final Result SKIP_CHILDREN = new Result(true, null);
public static Result skipTo(@NotNull VirtualFile parentToSkipTo) {
return new Result(true, parentToSkipTo);
}
private boolean myFollowSymLinks = true;
private boolean mySkipRoot = false;
private int myDepthLimit = -1;
private int myLevel = -1;
private Stack<Map<Key, Object>> myParameters = null;
protected VirtualFileVisitor(Option... options) {
for (Option option : options) {
if (option == NO_FOLLOW_SYMLINKS) myFollowSymLinks = false;
else if (option == SKIP_ROOT) mySkipRoot = true;
else if (option instanceof Option.LimitOption) myDepthLimit = ((Option.LimitOption)option).limit;
}
}
/**
* Simple visiting method.
* On returning {@code true} a visitor will proceed to file's children, on {@code false} - to file's next sibling.
*
* @param file a file to visit.
* @return {@code true} to proceed to file's children, {@code false} to skip to file's next sibling.
*/
public boolean visitFile(@NotNull VirtualFile file) {
return true;
}
/**
* Extended visiting method.
*
* @param file a file to visit.
* @return {@linkplain #CONTINUE} to proceed to file's children,<br/>
* {@linkplain #SKIP_CHILDREN} to skip to file's next sibling,<br/>
* result of {@linkplain #skipTo(VirtualFile)} to skip to given file's next sibling.
*/
@NotNull
public Result visitFileEx(@NotNull VirtualFile file) {
return visitFile(file) ? CONTINUE : SKIP_CHILDREN;
}
/**
* This method is only called if visiting wasn't interrupted (by returning skip-requesting result
* from {@linkplain #visitFile(VirtualFile)} or {@linkplain #visitFileEx(VirtualFile)} methods).
*
* @param file a file whose children were successfully visited.
*/
public void afterChildrenVisited(@NotNull VirtualFile file) { }
/**
* By default, visitor uses ({@linkplain com.intellij.openapi.vfs.VirtualFile#getChildren()}) to iterate over file's children.
* You can override this method to implement another mechanism.
*
* @param file a virtual file to get children from.
* @return children iterable, or null to use {@linkplain com.intellij.openapi.vfs.VirtualFile#getChildren()}.
*/
@Nullable
public Iterable<VirtualFile> getChildrenIterable(@NotNull VirtualFile file) {
return null;
}
public final <T> void set(@NotNull Key<T> parameter, @Nullable T value) {
if (myParameters == null) {
myParameters = ContainerUtil.newStack();
}
final Map<Key, Object> frame;
if (myParameters.isEmpty()) {
myParameters.push(frame = ContainerUtil.newHashMap());
}
else {
frame = myParameters.peek();
}
frame.put(parameter, value);
}
@Nullable
public final <T> T get(@NotNull Key<T> parameter) {
if (myParameters == null || myParameters.isEmpty()) {
return null;
}
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"}) final T value = (T)myParameters.peek().get(parameter);
return value;
}
final boolean allowVisitFile(@SuppressWarnings("UnusedParameters") @NotNull VirtualFile file) {
return myLevel > 0 || !mySkipRoot;
}
final boolean allowVisitChildren(@NotNull VirtualFile file) {
return !file.isSymLink() || (myFollowSymLinks && !VfsUtilCore.isInvalidLink(file));
}
final boolean depthLimitReached() {
return myDepthLimit >= 0 && myLevel >= myDepthLimit;
}
final void pushFrame() {
++myLevel;
if (myParameters != null && !myParameters.isEmpty()) {
final Map<Key, Object> lastFrame = myParameters.peek();
myParameters.push(new HashMap<Key, Object>() {
@Override
public Object get(Object key) {
return containsKey(key) ? super.get(key) : lastFrame.get(key);
}
});
}
}
final void popFrame() {
--myLevel;
if (myParameters != null && !myParameters.isEmpty()) {
myParameters.pop();
}
}
}
@@ -443,7 +443,6 @@ public class FileDocumentManagerImplTest extends PlatformLiteFixture {
myDocumentManager.myReloadFromDisk = Boolean.TRUE;
try {
file.setActualTimeStamp(file.getTimeStamp() + 1);
myDocumentManager.saveAllDocuments();
long fileStamp = file.getModificationStamp();
@@ -476,7 +475,6 @@ public class FileDocumentManagerImplTest extends PlatformLiteFixture {
file.setContent(null, "xxx", false);
file.setActualTimeStamp(file.getTimeStamp() + 1);
myDocumentManager.saveAllDocuments();
assertEquals("zzztest", document.getText());
@@ -0,0 +1,348 @@
/*
* 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.vfs;
import com.intellij.mock.MockVirtualFile;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.NullableFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
public class VirtualFileVisitorTest {
private static VirtualFile myRoot;
@BeforeClass
public static void setUp() throws Exception {
myRoot =
dir("/",
dir("d1",
dir("d11",
file("f11.1"),
file("f11.2")),
file("f1.1"),
dir("d12",
file("f12.1"),
file("f12.2"))),
dir("d2",
file("f2.1"),
file("f2.2")));
}
@AfterClass
public static void tearDown() throws Exception {
myRoot = null;
}
@Test
public void visitAll() {
doTest(
null, null,
"-> /\n" +
" -> d1\n" +
" -> d11\n" +
" -> f11.1\n" +
" <- f11.1\n" +
" -> f11.2\n" +
" <- f11.2\n" +
" <- d11\n" +
" -> f1.1\n" +
" <- f1.1\n" +
" -> d12\n" +
" -> f12.1\n" +
" <- f12.1\n" +
" -> f12.2\n" +
" <- f12.2\n" +
" <- d12\n" +
" <- d1\n" +
" -> d2\n" +
" -> f2.1\n" +
" <- f2.1\n" +
" -> f2.2\n" +
" <- f2.2\n" +
" <- d2\n" +
"<- /\n");
}
@Test
public void skipChildrenForDirectory() {
doTest(
new Function<VirtualFile, Object>() {
@Override
public Object fun(VirtualFile file) {
return "d11".equals(file.getName()) ? VirtualFileVisitor.SKIP_CHILDREN : VirtualFileVisitor.CONTINUE;
}
},
null,
"-> /\n" +
" -> d1\n" +
" -> d11\n" +
" -> f1.1\n" +
" <- f1.1\n" +
" -> d12\n" +
" -> f12.1\n" +
" <- f12.1\n" +
" -> f12.2\n" +
" <- f12.2\n" +
" <- d12\n" +
" <- d1\n" +
" -> d2\n" +
" -> f2.1\n" +
" <- f2.1\n" +
" -> f2.2\n" +
" <- f2.2\n" +
" <- d2\n" +
" <- /\n");
}
@Test
public void skipChildrenForFiles() {
doTest(
new Function<VirtualFile, Object>() {
@Override
public Object fun(VirtualFile file) {
return file.isDirectory() ? VirtualFileVisitor.CONTINUE : VirtualFileVisitor.SKIP_CHILDREN;
}
},
null,
"-> /\n" +
" -> d1\n" +
" -> d11\n" +
" -> f11.1\n" +
" -> f11.2\n" +
" <- d11\n" +
" -> f1.1\n" +
" -> d12\n" +
" -> f12.1\n" +
" -> f12.2\n" +
" <- d12\n" +
" <- d1\n" +
" -> d2\n" +
" -> f2.1\n" +
" -> f2.2\n" +
" <- d2\n" +
" <- /\n");
}
@Test
public void skipToParent() {
final Ref<VirtualFile> skip = Ref.create();
doTest(
new Function<VirtualFile, Object>() {
@Override
public Object fun(VirtualFile file) {
if ("d1".equals(file.getName())) skip.set(file);
return "f11.1".equals(file.getName()) ? skip.get() : VirtualFileVisitor.CONTINUE;
}
},
null,
"-> /\n" +
" -> d1\n" +
" -> d11\n" +
" -> f11.1\n" +
" -> d2\n" +
" -> f2.1\n" +
" <- f2.1\n" +
" -> f2.2\n" +
" <- f2.2\n" +
" <- d2\n" +
" <- /\n");
}
@Test
public void skipToRoot() {
doTest(
new Function<VirtualFile, Object>() {
@Override
public Object fun(VirtualFile file) {
return "f11.1".equals(file.getName()) ? myRoot : VirtualFileVisitor.CONTINUE;
}
},
null,
"-> /\n" +
" -> d1\n" +
" -> d11\n" +
" -> f11.1\n");
}
@Test
public void abort() {
doTest(
new Function<VirtualFile, Object>() {
@Override
public Object fun(VirtualFile file) {
if ("f11.1".equals(file.getName())) {
throw new AbortException();
}
return VirtualFileVisitor.CONTINUE;
}
},
null,
"-> /\n" +
" -> d1\n" +
" -> d11\n" +
" -> f11.1\n");
}
@Test
public void parameters() {
final Key<String> KEY = Key.create("vfs.visitor.test.parameter");
VfsUtilCore.visitChildrenRecursively(myRoot, new VirtualFileVisitor() {
{
set(KEY, myRoot.getPath());
}
@Override
public boolean visitFile(@NotNull VirtualFile file) {
String expected = file == myRoot ? myRoot.getPath() : file.getParent().getPath();
assertEquals(expected, get(KEY));
if (file.isDirectory()) {
set(KEY, file.getPath());
}
return true;
}
});
}
@Test
public void depthLimit() {
doTest(
null, null,
"-> /\n" +
"<- /\n",
VirtualFileVisitor.limit(0)
);
doTest(
null, null,
"-> /\n" +
" -> d1\n" +
" <- d1\n" +
" -> d2\n" +
" <- d2\n" +
"<- /\n",
VirtualFileVisitor.ONE_LEVEL_DEEP
);
doTest(
null, null,
"-> d1\n" +
"<- d1\n" +
"-> d2\n" +
"<- d2\n",
VirtualFileVisitor.SKIP_ROOT, VirtualFileVisitor.ONE_LEVEL_DEEP);
}
@Test
public void customIterable() {
doTest(
null,
new NullableFunction<VirtualFile, Iterable<VirtualFile>>() {
@Override
public Iterable<VirtualFile> fun(VirtualFile file) {
return "d12".equals(file.getName()) ? Collections.singletonList(file.getChildren()[1]) : null;
}
},
"-> /\n" +
" -> d1\n" +
" -> d11\n" +
" -> f11.1\n" +
" <- f11.1\n" +
" -> f11.2\n" +
" <- f11.2\n" +
" <- d11\n" +
" -> f1.1\n" +
" <- f1.1\n" +
" -> d12\n" +
" -> f12.2\n" +
" <- f12.2\n" +
" <- d12\n" +
" <- d1\n" +
" -> d2\n" +
" -> f2.1\n" +
" <- f2.1\n" +
" -> f2.2\n" +
" <- f2.2\n" +
" <- d2\n" +
"<- /\n");
}
private static class AbortException extends RuntimeException { }
private static void doTest(@Nullable final Function<VirtualFile, Object> condition,
@Nullable final Function<VirtualFile, Iterable<VirtualFile>> iterable,
@NotNull String expected,
VirtualFileVisitor.Option... options) {
final StringBuilder sb = new StringBuilder();
try {
VfsUtilCore.visitChildrenRecursively(myRoot, new VirtualFileVisitor(options) {
private int level = 0;
@NotNull
@Override
public Result visitFileEx(@NotNull VirtualFile file) {
sb.append(StringUtil.repeat(" ", level++)).append("-> ").append(file.getName()).append('\n');
if (condition != null) {
Object result = condition.fun(file);
if (result instanceof Result) return (Result)result;
if (result instanceof VirtualFile) return skipTo((VirtualFile)result);
}
return CONTINUE;
}
@Override
public void afterChildrenVisited(@NotNull VirtualFile file) {
sb.append(StringUtil.repeat(" ", --level)).append("<- ").append(file.getName()).append('\n');
}
@Nullable
@Override
public Iterable<VirtualFile> getChildrenIterable(@NotNull VirtualFile file) {
return iterable != null ? iterable.fun(file) : super.getChildrenIterable(file);
}
});
}
catch (AbortException ignore) { }
assertEquals(expected, sb.toString());
}
private static MockVirtualFile dir(@NotNull String name, MockVirtualFile... children) {
final MockVirtualFile root = new MockVirtualFile(true, name);
for (MockVirtualFile child : children) {
root.addChild(child);
}
return root;
}
private static MockVirtualFile file(@NotNull String name) {
return new MockVirtualFile(name);
}
}
@@ -389,7 +389,7 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase {
}));
final Set<String> actualSet = new HashSet<String>();
VfsUtilCore.visitChildrenRecursively(vDir, new VirtualFileVisitor(true) {
VfsUtilCore.visitChildrenRecursively(vDir, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
actualSet.add(file.getPath());
@@ -13,38 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.roots.impl;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileFilter;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import org.jetbrains.annotations.NotNull;
public class FileIndexImplUtil {
private FileIndexImplUtil() {
private FileIndexImplUtil() { }
public static boolean iterateRecursively(@NotNull final VirtualFile root,
@NotNull final VirtualFileFilter filter,
@NotNull final ContentIterator iterator) {
return VfsUtilCore.iterateChildrenRecursively(root, filter, iterator);
}
public static boolean iterateRecursively(@NotNull final VirtualFile root, @NotNull final VirtualFileFilter filter, @NotNull final ContentIterator iterator){
try {
VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
if (!file.isValid() || !filter.accept(file)) return false;
if (!iterator.processFile(file)) throw new StopItException();
return true;
}
});
return true;
}
catch (StopItException e) {
return false;
}
}
private static class StopItException extends RuntimeException {}
}
@@ -38,6 +38,7 @@ public class MockVirtualFile extends VirtualFile {
private final List<VirtualFile> myChildren = new SmartList<VirtualFile>();
private String myText;
private final MockVirtualFileSystem myFileSystem = new MockVirtualFileSystem();
private boolean myIsWritable = true;
private long myModStamp = LocalTimeCounter.currentTime();
public MockVirtualFile(final String name) {
@@ -73,11 +74,15 @@ public class MockVirtualFile extends VirtualFile {
@Override
public VirtualFile createChildData(final Object requestor, @NotNull @NonNls final String name) {
final MockVirtualFile file = new MockVirtualFile(name);
file.setParent(this);
myChildren.add(file);
addChild(file);
return file;
}
public void addChild(@NotNull final MockVirtualFile child) {
child.setParent(this);
myChildren.add(child);
}
@Override
@NotNull
public VirtualFileSystem getFileSystem() {
@@ -90,11 +95,11 @@ public class MockVirtualFile extends VirtualFile {
return prefix + "/" + myName;
}
private boolean myIsWritable = true;
@Override
public boolean isWritable() {
return myIsWritable;
}
public void setWritable(boolean b) {
myIsWritable = b;
}
@@ -117,7 +122,7 @@ public class MockVirtualFile extends VirtualFile {
@Override
public VirtualFile[] getChildren() {
return VfsUtil.toVirtualFileArray(myChildren);
return VfsUtilCore.toVirtualFileArray(myChildren);
}
@Override
@@ -161,16 +166,6 @@ public class MockVirtualFile extends VirtualFile {
@Override
public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) {
}
private long myActualTimeStamp = myTimeStamp;
public void setActualTimeStamp(long actualTimeStamp) {
myActualTimeStamp = actualTimeStamp;
}
public long getActualTimeStamp() {
return myActualTimeStamp;
}
@Override
@@ -191,6 +186,4 @@ public class MockVirtualFile extends VirtualFile {
myListener.contentsChanged(new VirtualFileEvent(requestor, this, null, oldStamp, myModStamp));
}
}
}
@@ -127,6 +127,21 @@ public class ContainerUtilRt {
return 5 + size + size / 5;
}
@NotNull
public static <T> Stack<T> newStack() {
return new Stack<T>();
}
@NotNull
public static <T> Stack<T> newStack(Collection<T> initial) {
return new Stack<T>(initial);
}
@NotNull
public static <T> Stack<T> newStack(T... initial) {
return new Stack<T>(Arrays.asList(initial));
}
/**
* Optimized toArray() as opposed to the {@link java.util.Collections#emptyList()}.
*/
@@ -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,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author max
*/
package com.intellij.util.containers;
import java.util.*;
/**
* A drop-in replacement for {@link java.util.Stack} based on {@link java.util.ArrayList} (instead of {@link java.util.Vector})
* and therefore is (1) not synchronized and (2) faster.
*
* @author max
*/
public class Stack<T> extends ArrayList<T> {
public Stack(int initialCapacity) {
super(initialCapacity);
@@ -1490,6 +1490,21 @@ public class ContainerUtil extends ContainerUtilRt {
return list;
}
@NotNull
public static <T> Stack<T> newStack() {
return ContainerUtilRt.newStack();
}
@NotNull
public static <T> Stack<T> newStack(Collection<T> initial) {
return ContainerUtilRt.newStack(initial);
}
@NotNull
public static <T> Stack<T> newStack(T... initial) {
return ContainerUtilRt.newStack(initial);
}
@NotNull
public static <T> List<T> emptyList() {
return ContainerUtilRt.emptyList();
@@ -165,29 +165,24 @@ public class VcsRootIterator {
}
public void iterate() {
class StopIterationException extends RuntimeException { }
try {
VfsUtilCore.visitChildrenRecursively(myRoot, new VirtualFileVisitor(false) {
@Override
public void afterChildrenVisited(@NotNull VirtualFile file) {
if (myDirectoryFilter != null) {
myDirectoryFilter.afterChildrenVisited(file);
}
VfsUtilCore.visitChildrenRecursively(myRoot, new VirtualFileVisitor(VirtualFileVisitor.NO_FOLLOW_SYMLINKS) {
@Override
public void afterChildrenVisited(@NotNull VirtualFile file) {
if (myDirectoryFilter != null) {
myDirectoryFilter.afterChildrenVisited(file);
}
}
@Override
public boolean visitFile(@NotNull VirtualFile file) {
if (isExcluded(myExcludedFileIndex, file)) return false;
if (myRootPresentFilter != null && ! myRootPresentFilter.accept(file)) return false;
if (myProject.isDisposed() || ! process(file)) throw new StopIterationException();
if (myDirectoryFilter != null && file.isDirectory() && ! myDirectoryFilter.shouldGoIntoDirectory(file)) return false;
return true;
}
});
} catch (StopIterationException e) {
//
}
@NotNull
@Override
public Result visitFileEx(@NotNull VirtualFile file) {
if (isExcluded(myExcludedFileIndex, file)) return SKIP_CHILDREN;
if (myRootPresentFilter != null && ! myRootPresentFilter.accept(file)) return SKIP_CHILDREN;
if (myProject.isDisposed() || ! process(file)) return skipTo(myRoot);
if (myDirectoryFilter != null && file.isDirectory() && ! myDirectoryFilter.shouldGoIntoDirectory(file)) return SKIP_CHILDREN;
return CONTINUE;
}
});
}
private boolean process(VirtualFile current) {