mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Add TreeUtil.accept to support TreeVisitor
This commit is contained in:
@@ -46,6 +46,7 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.intellij.openapi.wm.IdeFocusManager.getGlobalInstance;
|
||||
@@ -1137,4 +1138,79 @@ public final class TreeUtil {
|
||||
public static Comparator<TreePath> getDisplayOrderComparator(@NotNull final JTree tree) {
|
||||
return Comparator.comparingInt(tree::getRowForPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes nodes in the specified tree.
|
||||
*
|
||||
* @param tree a tree, which nodes should be processed
|
||||
* @param visitor a visitor that controls processing of tree nodes
|
||||
* @param consumer a path consumer called on done
|
||||
*/
|
||||
public static void accept(@NotNull JTree tree, @NotNull TreeVisitor visitor, @Nullable Consumer<TreePath> consumer) {
|
||||
Function<TreeVisitor, Promise<TreePath>> acceptor = getTreeAcceptor(tree);
|
||||
if (acceptor != null) {
|
||||
Promise<TreePath> promise = acceptor.apply(visitor);
|
||||
if (consumer != null) promise.processed(path -> consumer.accept(path));
|
||||
}
|
||||
else {
|
||||
TreeModel model = tree.getModel();
|
||||
TreePath path = model == null ? null : accept(model, visitor);
|
||||
if (consumer != null) consumer.accept(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static TreePath accept(@NotNull TreeModel model, @NotNull TreeVisitor visitor) {
|
||||
Object root = model.getRoot();
|
||||
if (root != null) {
|
||||
TreePath path = new TreePath(root);
|
||||
switch (visitor.visit(path)) {
|
||||
case INTERRUPT:
|
||||
return path; // path is found
|
||||
case CONTINUE:
|
||||
return accept(model, visitor, path);
|
||||
default:
|
||||
break; // skip children and/or siblings
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TreePath accept(@NotNull TreeModel model, @NotNull TreeVisitor visitor, @NotNull TreePath path) {
|
||||
ArrayDeque<ArrayDeque<TreePath>> stack = new ArrayDeque<>();
|
||||
stack.push(children(model, path));
|
||||
while (path != null) {
|
||||
ArrayDeque<TreePath> siblings = stack.peek();
|
||||
if (siblings == null) return null; // nothing to process
|
||||
|
||||
TreePath next = siblings.poll();
|
||||
if (next == null) {
|
||||
assert siblings == stack.poll();
|
||||
path = path.getParentPath();
|
||||
}
|
||||
else {
|
||||
switch (visitor.visit(next)) {
|
||||
case INTERRUPT:
|
||||
return next; // path is found
|
||||
case CONTINUE:
|
||||
stack.push(children(model, path = next));
|
||||
break;
|
||||
case SKIP_SIBLINGS:
|
||||
siblings.clear();
|
||||
break;
|
||||
case SKIP_CHILDREN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert stack.isEmpty();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ArrayDeque<TreePath> children(@NotNull TreeModel model, @NotNull TreePath path) {
|
||||
Object object = path.getLastPathComponent();
|
||||
int count = model.getChildCount(object);
|
||||
ArrayDeque<TreePath> deque = new ArrayDeque<>(count);
|
||||
for (int i = 0; i < count; i++) deque.add(path.pathByAddingChild(model.getChild(object, i)));
|
||||
return deque;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.ui.tree;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
class TreeModelWithDelay extends TreeModelWrapper {
|
||||
private final long delay;
|
||||
|
||||
TreeModelWithDelay(@NotNull TreeModel model, long delay) {
|
||||
super(model);
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
void pause() {
|
||||
if (delay > 0) {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRoot() {
|
||||
pause();
|
||||
return super.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChild(Object parent, int index) {
|
||||
pause();
|
||||
return super.getChild(parent, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildCount(Object parent) {
|
||||
pause();
|
||||
return super.getChildCount(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndexOfChild(Object parent, Object child) {
|
||||
pause();
|
||||
return super.getIndexOfChild(parent, child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeaf(Object child) {
|
||||
pause();
|
||||
return super.isLeaf(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueForPathChanged(TreePath path, Object value) {
|
||||
pause();
|
||||
super.valueForPathChanged(path, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.ui.tree;
|
||||
|
||||
import com.intellij.util.concurrency.Invoker;
|
||||
import com.intellij.util.concurrency.InvokerSupplier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.tree.TreeModel;
|
||||
|
||||
class TreeModelWithInvoker extends TreeModelWrapper implements InvokerSupplier {
|
||||
private final Invoker invoker;
|
||||
|
||||
TreeModelWithInvoker(@NotNull TreeModel model, @NotNull Invoker invoker) {
|
||||
super(model);
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Invoker getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.ui.tree;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.event.TreeModelListener;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
class TreeModelWrapper implements TreeModel {
|
||||
private final TreeModel model;
|
||||
|
||||
TreeModelWrapper(@NotNull TreeModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRoot() {
|
||||
return model.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChild(Object parent, int index) {
|
||||
return model.getChild(parent, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildCount(Object parent) {
|
||||
return model.getChildCount(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndexOfChild(Object parent, Object child) {
|
||||
return model.getIndexOfChild(parent, child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeaf(Object child) {
|
||||
return model.isLeaf(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueForPathChanged(TreePath path, Object value) {
|
||||
model.valueForPathChanged(path, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTreeModelListener(TreeModelListener listener) {
|
||||
model.addTreeModelListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTreeModelListener(TreeModelListener listener) {
|
||||
model.removeTreeModelListener(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.ui.tree;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.testFramework.PlatformTestUtil;
|
||||
import com.intellij.ui.tree.AsyncTreeModel;
|
||||
import com.intellij.ui.tree.TreeVisitor;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.concurrency.Invoker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.concurrency.AsyncPromise;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.awt.EventQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
*/
|
||||
public final class TreeUtilAcceptTest {
|
||||
@Test
|
||||
public void testAcceptDepth1() {
|
||||
testFind(() -> new DepthVisitor(1), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptDepth2() {
|
||||
testFind(() -> new DepthVisitor(2), 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptDepth3() {
|
||||
testFind(() -> new DepthVisitor(3), 21);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptAll() {
|
||||
testFind(() -> new Visitor(), 21);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFindRoot() {
|
||||
testFind(() -> new StringFinder("Root"), 1, "Root");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindWrongRoot() {
|
||||
testFind(() -> new StringFinder("ROOT"), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindColor() {
|
||||
testFind(() -> new StringFinder("RootColor"), 2, "RootColor");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindWrongColor() {
|
||||
testFind(() -> new StringFinder("RootCOLOR"), 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDigit() {
|
||||
testFind(() -> new StringFinder("RootDigit"), 3, "RootDigit");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindWrongDigit() {
|
||||
testFind(() -> new StringFinder("RootDIGIT"), 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindGreek() {
|
||||
testFind(() -> new StringFinder("RootGreek"), 4, "RootGreek");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindWrongGreek() {
|
||||
testFind(() -> new StringFinder("RootGREEK"), 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindGreen() {
|
||||
testFind(() -> new StringFinder("RootColorGreen"), 4, "RootColorGreen");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindWrongGreen() {
|
||||
testFind(() -> new StringFinder("RootColorGREEN"), 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindFive() {
|
||||
testFind(() -> new StringFinder("RootDigitFive"), 8, "RootDigitFive");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindWrongFive() {
|
||||
testFind(() -> new StringFinder("RootDigitFIVE"), 13);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindGamma() {
|
||||
testFind(() -> new StringFinder("RootGreekGamma"), 7, "RootGreekGamma");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindWrongGamma() {
|
||||
testFind(() -> new StringFinder("RootGreekGAMMA"), 9);
|
||||
}
|
||||
|
||||
private static void testFind(@NotNull Supplier<Visitor> supplier, long count) {
|
||||
testFind(supplier, count, null);
|
||||
}
|
||||
|
||||
private static void testFind(@NotNull Supplier<Visitor> supplier, long count, String value) {
|
||||
test((tree, promise) -> {
|
||||
@NotNull Visitor visitor = supplier.get();
|
||||
TreeUtil.accept(tree, visitor, path -> invokeSafely(promise, () -> {
|
||||
Assert.assertEquals(count, visitor.counter.get());
|
||||
Assert.assertEquals(value, value(path));
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testShowRoot() {
|
||||
testShow(() -> new StringFinder("Root"), "+[Root]\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowColor() {
|
||||
testShow(() -> new StringFinder("RootColor"), "-Root\n" +
|
||||
" +[RootColor]\n" +
|
||||
" +RootDigit\n" +
|
||||
" +RootGreek\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowDigit() {
|
||||
testShow(() -> new StringFinder("RootDigit"), "-Root\n" +
|
||||
" +RootColor\n" +
|
||||
" +[RootDigit]\n" +
|
||||
" +RootGreek\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowGreek() {
|
||||
testShow(() -> new StringFinder("RootGreek"), "-Root\n" +
|
||||
" +RootColor\n" +
|
||||
" +RootDigit\n" +
|
||||
" +[RootGreek]\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowGreen() {
|
||||
testShow(() -> new StringFinder("RootColorGreen"), "-Root\n" +
|
||||
" -RootColor\n" +
|
||||
" RootColorRed\n" +
|
||||
" [RootColorGreen]\n" +
|
||||
" RootColorBlue\n" +
|
||||
" +RootDigit\n" +
|
||||
" +RootGreek\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowFive() {
|
||||
testShow(() -> new StringFinder("RootDigitFive"), "-Root\n" +
|
||||
" +RootColor\n" +
|
||||
" -RootDigit\n" +
|
||||
" RootDigitOne\n" +
|
||||
" RootDigitTwo\n" +
|
||||
" RootDigitThree\n" +
|
||||
" RootDigitFour\n" +
|
||||
" [RootDigitFive]\n" +
|
||||
" RootDigitSix\n" +
|
||||
" RootDigitSeven\n" +
|
||||
" RootDigitEight\n" +
|
||||
" RootDigitNine\n" +
|
||||
" +RootGreek\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowGamma() {
|
||||
testShow(() -> new StringFinder("RootGreekGamma"), "-Root\n" +
|
||||
" +RootColor\n" +
|
||||
" +RootDigit\n" +
|
||||
" -RootGreek\n" +
|
||||
" RootGreekAlpha\n" +
|
||||
" RootGreekBeta\n" +
|
||||
" [RootGreekGamma]\n" +
|
||||
" RootGreekDelta\n" +
|
||||
" RootGreekEpsilon\n");
|
||||
}
|
||||
|
||||
private static void testShow(@NotNull Supplier<Visitor> supplier, @NotNull String expected) {
|
||||
test((tree, promise) -> {
|
||||
@NotNull Visitor visitor = supplier.get();
|
||||
TreeUtil.accept(tree, visitor, path -> invokeSafely(promise, () -> {
|
||||
Assert.assertNotNull(path);
|
||||
tree.makeVisible(path);
|
||||
tree.setSelectionPath(path);
|
||||
PlatformTestUtil.assertTreeEqual(tree, expected, true);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandOne() {
|
||||
test((tree, promise) -> TreeUtil.expand(tree, 1, () -> invokeSafely(promise, () -> PlatformTestUtil.assertTreeEqual(
|
||||
tree, "-Root\n" +
|
||||
" +RootColor\n" +
|
||||
" +RootDigit\n" +
|
||||
" +RootGreek\n"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandAll() {
|
||||
test((tree, promise) -> TreeUtil.expandAll(tree, () -> invokeSafely(promise, () -> PlatformTestUtil.assertTreeEqual(
|
||||
tree, "-Root\n" +
|
||||
" -RootColor\n" +
|
||||
" RootColorRed\n" +
|
||||
" RootColorGreen\n" +
|
||||
" RootColorBlue\n" +
|
||||
" -RootDigit\n" +
|
||||
" RootDigitOne\n" +
|
||||
" RootDigitTwo\n" +
|
||||
" RootDigitThree\n" +
|
||||
" RootDigitFour\n" +
|
||||
" RootDigitFive\n" +
|
||||
" RootDigitSix\n" +
|
||||
" RootDigitSeven\n" +
|
||||
" RootDigitEight\n" +
|
||||
" RootDigitNine\n" +
|
||||
" -RootGreek\n" +
|
||||
" RootGreekAlpha\n" +
|
||||
" RootGreekBeta\n" +
|
||||
" RootGreekGamma\n" +
|
||||
" RootGreekDelta\n" +
|
||||
" RootGreekEpsilon\n"))));
|
||||
}
|
||||
|
||||
|
||||
private static void invokeLater(@NotNull AsyncPromise<Throwable> promise, @NotNull Runnable runnable) {
|
||||
EventQueue.invokeLater(() -> invokeSafely(promise, runnable, false));
|
||||
}
|
||||
|
||||
private static void invokeSafely(@NotNull AsyncPromise<Throwable> promise, @NotNull Runnable runnable) {
|
||||
invokeSafely(promise, runnable, true);
|
||||
}
|
||||
|
||||
private static void invokeSafely(@NotNull AsyncPromise<Throwable> promise, @NotNull Runnable runnable, boolean stop) {
|
||||
try {
|
||||
runnable.run();
|
||||
if (stop) promise.setResult(null);
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
promise.setResult(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test(BiConsumer<JTree, AsyncPromise<Throwable>> consumer) {
|
||||
test(consumer, parent -> model(0, false, null));
|
||||
test(consumer, parent -> model(10, false, null));
|
||||
test(consumer, parent -> model(0, true, new Invoker.EDT(parent)));
|
||||
test(consumer, parent -> model(0, false, new Invoker.EDT(parent)));
|
||||
test(consumer, parent -> model(10, true, new Invoker.EDT(parent)));
|
||||
test(consumer, parent -> model(10, false, new Invoker.EDT(parent)));
|
||||
test(consumer, parent -> model(0, true, new Invoker.BackgroundThread(parent)));
|
||||
test(consumer, parent -> model(0, false, new Invoker.BackgroundThread(parent)));
|
||||
test(consumer, parent -> model(10, true, new Invoker.BackgroundThread(parent)));
|
||||
test(consumer, parent -> model(10, false, new Invoker.BackgroundThread(parent)));
|
||||
}
|
||||
|
||||
private static void test(BiConsumer<JTree, AsyncPromise<Throwable>> consumer, Function<Disposable, TreeModel> function) {
|
||||
if (EventQueue.isDispatchThread()) throw new IllegalThreadStateException("main thread is expected");
|
||||
AsyncPromise<Throwable> promise = new AsyncPromise<>();
|
||||
Disposable parent = Disposer.newDisposable();
|
||||
TreeModel model = function.apply(parent);
|
||||
AsyncTreeModel atm = model instanceof AsyncTreeModel ? (AsyncTreeModel)model : null;
|
||||
invokeLater(promise, () -> {
|
||||
@SuppressWarnings("UndesirableClassUsage")
|
||||
JTree tree = new JTree(model);
|
||||
if (atm != null) TreeUtil.setTreeAcceptor(tree, atm::accept);
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (atm != null && atm.isProcessing()) {
|
||||
invokeLater(promise, this);
|
||||
}
|
||||
else {
|
||||
tree.collapseRow(0); // because root node is expanded by default
|
||||
PlatformTestUtil.assertTreeEqual(tree, "+Root\n");
|
||||
consumer.accept(tree, promise);
|
||||
}
|
||||
}
|
||||
}.run();
|
||||
});
|
||||
try {
|
||||
Throwable throwable = promise.blockingGet(2, MINUTES);
|
||||
if (throwable != null) throw new IllegalStateException("test failed", throwable);
|
||||
}
|
||||
finally {
|
||||
Disposer.dispose(parent);
|
||||
}
|
||||
}
|
||||
|
||||
private static TreeModel model(long delay, boolean showLoadingNode, Invoker invoker) {
|
||||
TreeModel model = new DefaultTreeModel(root());
|
||||
if (delay > 0) {
|
||||
model = new TreeModelWithDelay(model, delay);
|
||||
}
|
||||
if (invoker != null) {
|
||||
model = new TreeModelWithInvoker(model, invoker);
|
||||
model = new AsyncTreeModel(model, showLoadingNode);
|
||||
Disposer.register(invoker, (Disposable)model);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private static String value(TreePath path) {
|
||||
return path == null ? null : path.getLastPathComponent().toString();
|
||||
}
|
||||
|
||||
private static DefaultMutableTreeNode node(@NotNull Object object, Object... children) {
|
||||
if (object instanceof DefaultMutableTreeNode && ArrayUtil.isEmpty(children)) return (DefaultMutableTreeNode)object;
|
||||
if (object instanceof TreeNode) throw new IllegalArgumentException("do not use a tree node as a node content");
|
||||
DefaultMutableTreeNode node = new DefaultMutableTreeNode(object);
|
||||
for (Object child : children) node.add(node(child));
|
||||
return node;
|
||||
}
|
||||
|
||||
private static DefaultMutableTreeNode root() {
|
||||
return node("Root",
|
||||
node("RootColor",
|
||||
node("RootColorRed"),
|
||||
node("RootColorGreen"),
|
||||
node("RootColorBlue")),
|
||||
node("RootDigit",
|
||||
node("RootDigitOne"),
|
||||
node("RootDigitTwo"),
|
||||
node("RootDigitThree"),
|
||||
node("RootDigitFour"),
|
||||
node("RootDigitFive"),
|
||||
node("RootDigitSix"),
|
||||
node("RootDigitSeven"),
|
||||
node("RootDigitEight"),
|
||||
node("RootDigitNine")),
|
||||
node("RootGreek",
|
||||
node("RootGreekAlpha"),
|
||||
node("RootGreekBeta"),
|
||||
node("RootGreekGamma"),
|
||||
node("RootGreekDelta"),
|
||||
node("RootGreekEpsilon")));
|
||||
}
|
||||
|
||||
static class Visitor extends TreeVisitor.Finder {
|
||||
final AtomicLong counter = new AtomicLong();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Action visit(@NotNull TreePath path) {
|
||||
counter.incrementAndGet();
|
||||
return super.visit(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean found(@NotNull TreePath path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean contains(@NotNull TreePath path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class DepthVisitor extends Visitor {
|
||||
private final int depth;
|
||||
|
||||
DepthVisitor(int depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean contains(@NotNull TreePath path) {
|
||||
return depth > path.getPathCount();
|
||||
}
|
||||
}
|
||||
|
||||
static class StringFinder extends Visitor {
|
||||
private final String value;
|
||||
|
||||
StringFinder(@NotNull String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean found(@NotNull TreePath path) {
|
||||
return value.equals(value(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean contains(@NotNull TreePath path) {
|
||||
return value.startsWith(value(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user