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 extends CtElement> elements) {
if (isTerminated() || elements == null) {
@@ -60,9 +96,29 @@ public void scan(Collection extends CtElement> 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);
+}