diff --git a/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java b/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java index 573e8114e11..e5d0e7026a7 100644 --- a/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java +++ b/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java @@ -17,15 +17,32 @@ package spoon.reflect.visitor; import spoon.reflect.declaration.CtElement; +import spoon.reflect.visitor.chain.CtScannerListener; import java.util.ArrayList; import java.util.Collection; import java.util.Map; +/** + * The extension of {@link CtScanner}, which supports early termination of scanning process. + * Is useful when your algorithm is searching for an node after node is found + * future scanning makes no sense. + * Then you can call {@link #terminate()}, which assures that no more AST nodes are visited, + * means no {@link #scan(CtElement)} method is called and the scanning algorithm finishes + * as soon as possible. + *
+ * There is possible to register an implementation of {@link CtScannerListener}, + * whose {@link CtScannerListener#enter(Object)}/{@link CtScannerListener#exit(Object)} + * methods are called before/after each AST node is visited.
+ * Note that the {@link CtScannerListener#enter(Object)} can influence whether AST node and it's children is visited. See it's documentation for details. + * + * @param the type of the result produced by this scanner. + */ public class EarlyTerminatingScanner extends CtScanner { private boolean terminate = false; private T result; + private CtScannerListener listener; protected void terminate() { terminate = true; @@ -39,10 +56,29 @@ protected void setResult(T result) { this.result = result; } + /** + * @return the result of scanning - the value, which was stored by previous call of {@link #setResult(Object)} + */ public T getResult() { return result; } + /** + * @return null or the implementation of {@link CtScannerListener}, which is registered to listen for enter/exit of nodes during scanning of AST + */ + public CtScannerListener getListener() { + return listener; + } + + /** + * @param listener the implementation of {@link CtScannerListener}, which will listen for enter/exit of nodes during scanning of AST + * @return this to support fluent API + */ + public EarlyTerminatingScanner setListener(CtScannerListener listener) { + this.listener = listener; + return this; + } + @Override public void scan(Collection elements) { if (isTerminated() || elements == null) { @@ -60,9 +96,29 @@ public void scan(Collection elements) { @Override public void scan(CtElement element) { - if (isTerminated()) { + if (element == null || isTerminated()) { return; } + if (listener == null) { + //the listener is not defined + //visit this element and may be children + doScan(element); + } else { + //the listener is defined, call it's enter method first + if (listener.enter(element)) { + //the listener decided to visit this element and may be children + doScan(element); + //then call exit, only if enter returned true + listener.exit(element); + } //else the listener decided to skip this element and all children. Do not call exit. + } + } + + /** + * This method is called ONLY when listener decides that element and children may be visited. + * It is needed to let child classes to override it and to react on that situation + */ + protected void doScan(CtElement element) { super.scan(element); } diff --git a/src/main/java/spoon/reflect/visitor/chain/CtScannerListener.java b/src/main/java/spoon/reflect/visitor/chain/CtScannerListener.java new file mode 100644 index 00000000000..21241696c43 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/chain/CtScannerListener.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2006-2017 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.reflect.visitor.chain; + +/** + * Listens for entering/exiting of scanning of AST + */ +public interface CtScannerListener { + /** + * Called before scanning process enters an element + *
+ * The returning false or throwing an exception causes that processing of that element and all it's children is skipped. + * The returning true causes that element is normally processed, and {@link #exit(Object)} will be called for that element. + * + * @param element the processed element + * @return true to continue processing this element and it's children, + * false to skip this element and it's children. + */ + boolean enter(Object element); + + /** + * This method is called after element and all it's children are processed. + * This method is NOT called if the exception was thrown in {@link #enter(Object)} or during processing of element or any children children + * This method is NOT called for element whose {@link #enter(Object)} returned false + * + * @param element the processed element + */ + void exit(Object element); +}