From 0b1de0924ef6e5050282c5137da641559a83caa6 Mon Sep 17 00:00:00 2001 From: Pavel Vojtechovsky Date: Wed, 8 Mar 2017 18:43:52 +0100 Subject: [PATCH 1/2] feature CtScannerListener listens for nodes of EarlyTerminatingScanner --- .../visitor/EarlyTerminatingScanner.java | 60 ++++++++++++++++++- .../visitor/chain/CtScannerListener.java | 44 ++++++++++++++ .../reflect/visitor/chain/ScanningMode.java | 43 +++++++++++++ 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/main/java/spoon/reflect/visitor/chain/CtScannerListener.java create mode 100644 src/main/java/spoon/reflect/visitor/chain/ScanningMode.java diff --git a/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java b/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java index 573e8114e11..b810b4139b6 100644 --- a/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java +++ b/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java @@ -17,15 +17,33 @@ package spoon.reflect.visitor; import spoon.reflect.declaration.CtElement; +import spoon.reflect.visitor.chain.CtScannerListener; +import spoon.reflect.visitor.chain.ScanningMode; 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(CtElement)}/{@link CtScannerListener#exit(CtElement)} + * methods are called before/after each AST node is visited.
+ * Note that the {@link CtScannerListener#enter(CtElement)} 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 +57,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 +97,30 @@ 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, ScanningMode.NORMAL); + } else { + //the listener is defined, call it's enter method first + ScanningMode mode = listener.enter(element); + if (mode != ScanningMode.SKIP_ALL) { + //the listener decided to visit this element and may be children + doScan(element, mode); + //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, ScanningMode mode) { 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..bb004994351 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/chain/CtScannerListener.java @@ -0,0 +1,44 @@ +/** + * 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; + +import spoon.reflect.declaration.CtElement; + +/** + * Listens for entering/exiting of scanning of AST + */ +public interface CtScannerListener { + /** + * Called before scanning process enters an element + *
+ * See {@link ScanningMode} documentation for possible modes + * The returning {@link ScanningMode#SKIP_ALL} causes that element and all children are skipped and {@link #exit(CtElement)} will be NOT called for that element. + * + * @param element the processed element + * @return {@link ScanningMode} to drive how scanner processes this element and it's children, + */ + ScanningMode enter(CtElement 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(CtElement)} or during processing of element or any children children + * This method is NOT called for element whose {@link #enter(CtElement)} returned {@link ScanningMode#SKIP_ALL} + * + * @param element the processed element + */ + void exit(CtElement element); +} diff --git a/src/main/java/spoon/reflect/visitor/chain/ScanningMode.java b/src/main/java/spoon/reflect/visitor/chain/ScanningMode.java new file mode 100644 index 00000000000..f0837c486d9 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/chain/ScanningMode.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; + +/** + * Use in {@link CtScannerListener#enter(spoon.reflect.declaration.CtElement)} to define how to continue with scanning + */ +public enum ScanningMode { + /** + * Continue with scanning in a normal way. Means current element and all children are visited. + */ + NORMAL(true, true), + /** + * Skip current element and all it's children. + */ + SKIP_ALL(false, false), + /** + * Visit current element but skip all it's children. + */ + SKIP_CHILDREN(true, false); + + public final boolean visitElement; + public final boolean visitChildren; + + ScanningMode(boolean visitElement, boolean visitChildren) { + this.visitElement = visitElement; + this.visitChildren = visitChildren; + } +} From 436e5b33d9da905f5f4712e3a9e6588cd35729a8 Mon Sep 17 00:00:00 2001 From: Martin Monperrus Date: Thu, 9 Mar 2017 19:14:45 +0100 Subject: [PATCH 2/2] update documentation --- .../visitor/EarlyTerminatingScanner.java | 23 ++++++++----------- .../visitor/chain/CtScannerListener.java | 20 ++++++++-------- .../reflect/visitor/chain/ScanningMode.java | 8 +++---- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java b/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java index b810b4139b6..b57c313e168 100644 --- a/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java +++ b/src/main/java/spoon/reflect/visitor/EarlyTerminatingScanner.java @@ -25,17 +25,13 @@ 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. + * Extends {@link CtScanner}, to support early termination of scanning process and scan listeners. + * It is useful when your algorithm is searching for a specific node only. + * In this case, you can call {@link #terminate()}, which ensures that no more AST nodes are visited, *
- * There is possible to register an implementation of {@link CtScannerListener}, + * It is possible to register an implementation of {@link CtScannerListener}, * whose {@link CtScannerListener#enter(CtElement)}/{@link CtScannerListener#exit(CtElement)} * methods are called before/after each AST node is visited.
- * Note that the {@link CtScannerListener#enter(CtElement)} 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. */ @@ -58,21 +54,22 @@ protected void setResult(T result) { } /** - * @return the result of scanning - the value, which was stored by previous call of {@link #setResult(Object)} + * @return the result of scanning - the value, which was stored by a 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 + * @return null or the implementation of {@link CtScannerListener}, which is registered to listen for enter/exit of nodes during scanning of the 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 + * @param listener the implementation of {@link CtScannerListener}, which will be called back when entering/exiting + * odes during scanning. * @return this to support fluent API */ public EarlyTerminatingScanner setListener(CtScannerListener listener) { @@ -117,8 +114,8 @@ public void scan(CtElement element) { } /** - * 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 + * This method is called ONLY when the listener decides that the current element and children should be visited. + * Subclasses can override it to react accordingly. */ protected void doScan(CtElement element, ScanningMode mode) { 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 index bb004994351..583318f1a88 100644 --- a/src/main/java/spoon/reflect/visitor/chain/CtScannerListener.java +++ b/src/main/java/spoon/reflect/visitor/chain/CtScannerListener.java @@ -19,26 +19,24 @@ import spoon.reflect.declaration.CtElement; /** - * Listens for entering/exiting of scanning of AST + * Responsible for performing an action when a scanner enters/exits a node while scanning the AST. */ public interface CtScannerListener { /** - * Called before scanning process enters an element - *
- * See {@link ScanningMode} documentation for possible modes - * The returning {@link ScanningMode#SKIP_ALL} causes that element and all children are skipped and {@link #exit(CtElement)} will be NOT called for that element. + * Called before the scanner enters an element * - * @param element the processed element - * @return {@link ScanningMode} to drive how scanner processes this element and it's children, + * @param element the element about to be scanned. + * @return a {@link ScanningMode} that drives how the scanner processes this element and its children. + * For instance, returning {@link ScanningMode#SKIP_ALL} causes that element and all children to be skipped and {@link #exit(CtElement)} are be NOT called for that element. */ ScanningMode enter(CtElement 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(CtElement)} or during processing of element or any children children - * This method is NOT called for element whose {@link #enter(CtElement)} returned {@link ScanningMode#SKIP_ALL} + * This method is called after the element and all its children have been visited. + * This method is NOT called if an exception is thrown in {@link #enter(CtElement)} or during the scanning of the element or any of its children element. + * This method is NOT called for an element for which {@link #enter(CtElement)} returned {@link ScanningMode#SKIP_ALL}. * - * @param element the processed element + * @param element the element that has just been scanned. */ void exit(CtElement element); } diff --git a/src/main/java/spoon/reflect/visitor/chain/ScanningMode.java b/src/main/java/spoon/reflect/visitor/chain/ScanningMode.java index f0837c486d9..955b0a33154 100644 --- a/src/main/java/spoon/reflect/visitor/chain/ScanningMode.java +++ b/src/main/java/spoon/reflect/visitor/chain/ScanningMode.java @@ -17,19 +17,19 @@ package spoon.reflect.visitor.chain; /** - * Use in {@link CtScannerListener#enter(spoon.reflect.declaration.CtElement)} to define how to continue with scanning + * Defines how a {@link CtScannerListener} drives the scanning of {@link spoon.reflect.visitor.EarlyTerminatingScanner} */ public enum ScanningMode { /** - * Continue with scanning in a normal way. Means current element and all children are visited. + * Continue with scanning in a normal way, the current element and all children are visited. */ NORMAL(true, true), /** - * Skip current element and all it's children. + * Skip the current element and skip all its children. */ SKIP_ALL(false, false), /** - * Visit current element but skip all it's children. + * Visit current element but skips all its children. */ SKIP_CHILDREN(true, false);