From ccbdced117567944709a04d8f0759e6c20623a64 Mon Sep 17 00:00:00 2001
From: Pavel Vojtechovsky
Date: Wed, 10 May 2017 20:42:43 +0200
Subject: [PATCH 1/4] feature: SubInheritanceHierarchyFunction/Resolver
---
.../SubInheritanceHierarchyFunction.java | 124 ++++++++
.../SubInheritanceHierarchyResolver.java | 280 ++++++++++++++++++
2 files changed, 404 insertions(+)
create mode 100644 src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java
create mode 100644 src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
diff --git a/src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java b/src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java
new file mode 100644
index 00000000000..82aaf200ba1
--- /dev/null
+++ b/src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java
@@ -0,0 +1,124 @@
+/**
+ * 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.filter;
+
+import spoon.reflect.declaration.CtElement;
+import spoon.reflect.declaration.CtType;
+import spoon.reflect.declaration.CtTypeInformation;
+import spoon.reflect.reference.CtTypeReference;
+import spoon.reflect.visitor.chain.CtConsumableFunction;
+import spoon.reflect.visitor.chain.CtConsumer;
+import spoon.reflect.visitor.chain.CtQuery;
+import spoon.reflect.visitor.chain.CtQueryAware;
+import spoon.support.visitor.SubInheritanceHierarchyResolver;
+
+/**
+ * Expects a {@link CtTypeInformation} as input
+ * and produces all sub classes and sub interfaces recursively.
+ * The output is produced in arbitrary order.
+ */
+public class SubInheritanceHierarchyFunction implements CtConsumableFunction, CtQueryAware {
+
+ private boolean includingSelf = false;
+ private boolean includingInterfaces = true;
+ private CtQuery query;
+ private boolean failOnClassNotFound = false;
+ private boolean returnTypeReferences = false;
+
+ /**
+ * The mapping function created using this constructor
+ * will visit each super class and super interface
+ * following super hierarchy. It can happen
+ * that some interfaces will be visited more then once
+ * if they are in super inheritance hierarchy more then once.
+ * Use second constructor if you want to visit each interface only once.
+ */
+ public SubInheritanceHierarchyFunction() {
+ }
+
+ /**
+ * @param includingSelf if true then input element is sent to output too. By default it is false.
+ */
+ public SubInheritanceHierarchyFunction includingSelf(boolean includingSelf) {
+ this.includingSelf = includingSelf;
+ return this;
+ }
+
+ /**
+ * @param includingInterfaces if false then interfaces are not visited - only super classes. By default it is true.
+ */
+ public SubInheritanceHierarchyFunction includingInterfaces(boolean includingInterfaces) {
+ this.includingInterfaces = includingInterfaces;
+ return this;
+ }
+
+ /**
+ * configures whether {@link CtType} or {@link CtTypeReference} instances are returned by this mapping function
+ * @param returnTypeReferences if true then {@link CtTypeReference} instances are returned by this mapping function
+ * @return this to support fluent API
+ */
+ public SubInheritanceHierarchyFunction returnTypeReferences(boolean returnTypeReferences) {
+ this.returnTypeReferences = returnTypeReferences;
+ return this;
+ }
+
+ /**
+ * @param failOnClassNotFound sets whether processing should throw an exception if class is missing in noClassPath mode
+ */
+ public SubInheritanceHierarchyFunction failOnClassNotFound(boolean failOnClassNotFound) {
+ this.failOnClassNotFound = failOnClassNotFound;
+ return this;
+ }
+
+ @Override
+ public void apply(CtTypeInformation input, final CtConsumer outputConsumer) {
+ final SubInheritanceHierarchyResolver fnc = new SubInheritanceHierarchyResolver(input)
+ .failOnClassNotFound(failOnClassNotFound)
+ .includingInterfaces(includingInterfaces)
+ .returnTypeReferences(returnTypeReferences);
+ if (includingSelf) {
+ if (returnTypeReferences) {
+ if (input instanceof CtTypeReference) {
+ outputConsumer.accept(input);
+ } else {
+ outputConsumer.accept(((CtType>) input).getReference());
+ }
+ } else {
+ if (input instanceof CtTypeReference) {
+ outputConsumer.accept(((CtTypeReference>) input).getTypeDeclaration());
+ } else {
+ outputConsumer.accept(((CtType>) input));
+ }
+ }
+ }
+ fnc.forEachSubTypeInPackage(((CtElement) input).getFactory().getModel().getRootPackage(), new CtConsumer() {
+ @Override
+ public void accept(CtTypeInformation typeInfo) {
+ outputConsumer.accept(typeInfo);
+ if (query.isTerminated()) {
+ fnc.terminate();
+ }
+ }
+ });
+ }
+
+
+ @Override
+ public void setQuery(CtQuery query) {
+ this.query = query;
+ }
+}
diff --git a/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
new file mode 100644
index 00000000000..a57bd08da14
--- /dev/null
+++ b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
@@ -0,0 +1,280 @@
+/**
+ * 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.support.visitor;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Set;
+
+import spoon.SpoonException;
+import spoon.reflect.declaration.CtClass;
+import spoon.reflect.declaration.CtElement;
+import spoon.reflect.declaration.CtEnum;
+import spoon.reflect.declaration.CtPackage;
+import spoon.reflect.declaration.CtType;
+import spoon.reflect.declaration.CtTypeInformation;
+import spoon.reflect.declaration.CtTypeParameter;
+import spoon.reflect.reference.CtTypeReference;
+import spoon.reflect.visitor.Filter;
+import spoon.reflect.visitor.chain.CtConsumer;
+import spoon.reflect.visitor.chain.CtQuery;
+import spoon.reflect.visitor.chain.CtScannerListener;
+import spoon.reflect.visitor.chain.ScanningMode;
+import spoon.reflect.visitor.filter.CtScannerFunction;
+import spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction;
+import spoon.reflect.visitor.filter.TypeFilter;
+
+import static spoon.reflect.visitor.chain.ScanningMode.NORMAL;
+import static spoon.reflect.visitor.chain.ScanningMode.SKIP_ALL;
+
+/**
+ * Expects a {@link CtPackage} as input
+ * and produces all sub classes and sub interfaces, which extends or implements super type(s) provided in constructor and stored as `targetSuperTypes`.
+ * The input `targetSuperTypes` itself are not returned as result.
+ * The output is produced in arbitrary order.
+ *
+ * The repeated processing of this mapping function on the same input returns only newly found sub types.
+ * The instance of {@link SubInheritanceHierarchyResolver} returns found sub types only once.
+ * So repeated call with same input package returns nothing.
+ * Create and use new instance of {@link SubInheritanceHierarchyResolver} if you need to scan the subtype hierarchy again.
+ */
+public class SubInheritanceHierarchyResolver {
+ private boolean includingInterfaces = true;
+ /**
+ * Set of qualified names of all super types whose sub types we are searching for.
+ * Each found sub type is added to this set too
+ */
+ private Set targetSuperTypes = new HashSet<>();
+ /**
+ * if true then we have to check if type is a subtype of superClass or superInterfaces too
+ * if false then it is enough to search in superClass hierarchy only (faster)
+ */
+ boolean hasSuperInterface = false;
+ private boolean failOnClassNotFound = false;
+ private boolean returnTypeReferences = false;
+
+ private boolean terminated = false;
+
+ /**
+ * @param superType target super type
+ */
+ public SubInheritanceHierarchyResolver(CtTypeInformation superType) {
+ addSuperType(superType);
+ }
+
+ /**
+ * @return set of qualified names of types, of all super types and all found sub types
+ */
+ public Set getHierarchyTypesQualifiedNames() {
+ return targetSuperTypes;
+ }
+
+ /**
+ * Add another super type to this mapping function.
+ * Using this function you can search parallel in more sub type hierarchies.
+ *
+ * @param superType - the type whose sub types will be returned by this mapping function too.
+ */
+ public SubInheritanceHierarchyResolver addSuperType(CtTypeInformation superType) {
+ targetSuperTypes.add(superType.getQualifiedName());
+ if (hasSuperInterface == false) {
+ hasSuperInterface = superType.isInterface();
+ }
+ return this;
+ }
+
+ /**
+ * @param includingInterfaces if false then interfaces are not visited - only super classes. By default it is true.
+ */
+ public SubInheritanceHierarchyResolver includingInterfaces(boolean includingInterfaces) {
+ this.includingInterfaces = includingInterfaces;
+ return this;
+ }
+
+ /**
+ * configures whether {@link CtType} or {@link CtTypeReference} instances are returned by this mapping function
+ * @param returnTypeReferences if true then {@link CtTypeReference} instances are returned by this mapping function
+ * @return this to support fluent API
+ */
+ public SubInheritanceHierarchyResolver returnTypeReferences(boolean returnTypeReferences) {
+ this.returnTypeReferences = returnTypeReferences;
+ return this;
+ }
+
+ /**
+ * @param failOnClassNotFound sets whether processing should throw an exception if class is missing in noClassPath mode
+ */
+ public SubInheritanceHierarchyResolver failOnClassNotFound(boolean failOnClassNotFound) {
+ this.failOnClassNotFound = failOnClassNotFound;
+ return this;
+ }
+
+ /**
+ * Terminates the evaluation.
+ */
+ public void terminate() {
+ terminated = true;
+ }
+
+ /**
+ * @return true if the evaluation has been terminated.
+ */
+ public boolean isTerminated() {
+ return terminated;
+ }
+
+ /**
+ * Calls `outputConsumer.apply(subType)` for each sub type found in `inputPackage`.
+ * Each sub type is returned only once.
+ * If {@link #forEachSubTypeInPackage(CtPackage, CtConsumer)} is called again with same input and configuration
+ * on the same instance of {@link SubInheritanceHierarchyResolver} and on the same model,
+ * then it returns no sub types, because there are no NEW subtypes.
+ * It makes sense to call this method again for example after new super types are added
+ * by {@link #addSuperType(CtTypeInformation)}.
+ * @param inputPackage the package where sub types are searched for
+ * @param outputConsumer the consumer for found sub types
+ */
+ public void forEachSubTypeInPackage(CtPackage inputPackage, final CtConsumer outputConsumer) {
+ /*
+ * Set of qualified names of all visited types, independent on whether they are sub types or not.
+ */
+ final Set allVisitedTypeNames = new HashSet<>();
+ /*
+ * the queue of types whose super inheritance hierarchy we are just visiting.
+ * They are potential sub types of an `targetSuperTypes`
+ */
+ final Deque> currentSubTypes = new ArrayDeque<>();
+ //algorithm
+ //1) query step: scan input package for sub classes and sub interfaces
+ final CtQuery q = inputPackage.map(new CtScannerFunction());
+ //2) query step: visit only required CtTypes
+ if (includingInterfaces) {
+ //the client is interested in sub inheritance hierarchy of interfaces too. Check interfaces, classes, enums, Annotations, but not CtTypeParameters.
+ q.select(typeFilter);
+ } else {
+ //the client is not interested in sub inheritance hierarchy of interfaces. Check only classes and enums.
+ q.select(classFilter);
+ }
+ /*
+ * 3) query step: for each found CtType, visit it's super inheritance hierarchy and search there for a type which is equal to one of targetSuperTypes.
+ * If found then all sub types in hierarchy (variable `currentSubTypes`) are sub types of targetSuperTypes. So return them
+ */
+ q.map(new SuperInheritanceHierarchyFunction()
+ //if there is any interface between `targetSuperTypes`, then we have to check superInterfaces too
+ .includingInterfaces(hasSuperInterface)
+ //internally it works always with references
+ .returnTypeReferences(true)
+ .failOnClassNotFound(failOnClassNotFound)
+ /*
+ * listen for types in super inheritance hierarchy
+ * 1) to collect `currentSubTypes`
+ * 2) to check if we have already found a targetSuperType
+ * 3) if found then send `currentSubTypes` to `outputConsumer` and skip visiting of further super types
+ */
+ .setListener(new CtScannerListener() {
+ @Override
+ public ScanningMode enter(CtElement element) {
+ if (terminated) {
+ q.terminate();
+ return SKIP_ALL;
+ }
+ CtTypeReference> typeRef = (CtTypeReference>) element;
+ String qName = typeRef.getQualifiedName();
+ if (targetSuperTypes.contains(qName)) {
+ /*
+ * FOUND! we are in super inheritance hierarchy, which extends from an searched super type(s).
+ * All `currentSubTypes` are sub types of searched super type
+ */
+ while (currentSubTypes.size() > 0) {
+ typeRef = currentSubTypes.pop();
+ /*
+ * Send them to outputConsumer and add then as targetSuperTypes too, to perform faster with detection of next sub types.
+ */
+ sendResult(typeRef, outputConsumer);
+ }
+ //we do not have to go deeper into super inheritance hierarchy. Skip visiting of further super types
+ //but continue visiting of siblings (do not terminate query)
+ return SKIP_ALL;
+ }
+ if (allVisitedTypeNames.add(qName) == false) {
+ /*
+ * this type was already visited, by another way. So it is not sub type of `targetSuperTypes`.
+ * Stop visiting it's inheritance hierarchy.
+ */
+ return SKIP_ALL;
+ }
+ /*
+ * This type was not visited yet.
+ * We still do not know whether this type is a sub type of any target super type(s)
+ * continue searching in super inheritance hierarchy
+ */
+ currentSubTypes.push(typeRef);
+ return NORMAL;
+ }
+ @Override
+ public void exit(CtElement element) {
+ CtTypeInformation type = (CtTypeInformation) element;
+ if (currentSubTypes.isEmpty() == false) {
+ //remove current type, which is not a sub type of targetSuperTypes from the currentSubTypes
+ CtTypeInformation stackType = currentSubTypes.pop();
+ if (stackType != type) {
+ //the enter/exit was not called consistently. There is a bug in SuperInheritanceHierarchyFunction
+ throw new SpoonException("CtScannerListener#exit was not called after enter.");
+ }
+ }
+ }
+ })
+ ).forEach(new CtConsumer>() {
+ @Override
+ public void accept(CtType> type) {
+ //we do not care about types visited by query `q`.
+ //the result of whole mapping function was already produced by `sendResult` call
+ //but we have to consume all these results to let query running
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ private void sendResult(CtTypeReference> typeRef, CtConsumer outputConsumer) {
+ if (targetSuperTypes.add(typeRef.getQualifiedName())) {
+ if (returnTypeReferences) {
+ outputConsumer.accept((T) typeRef);
+ } else {
+ outputConsumer.accept((T) typeRef.getTypeDeclaration());
+ }
+ }
+ }
+
+ /**
+ * accept all {@link CtType} excluding {@link CtTypeParameter}
+ */
+ private static final Filter> typeFilter = new Filter>() {
+ @Override
+ public boolean matches(CtType> type) {
+ if (type instanceof CtTypeParameter) {
+ return false;
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Accept all {@link CtClass}, {@link CtEnum}
+ */
+ private static final Filter> classFilter = new TypeFilter>(CtClass.class);
+}
From c9530f1bb6d8d0326253b3ee3fe56ea3e7e828e8 Mon Sep 17 00:00:00 2001
From: Martin Monperrus
Date: Thu, 18 May 2017 21:24:33 +0200
Subject: [PATCH 2/4] add test for SubInheritanceHierarchyResolver
---
.../SubInheritanceHierarchyFunction.java | 124 ------------------
.../SubInheritanceHierarchyResolver.java | 78 ++---------
.../java/spoon/test/filters/FilterTest.java | 58 ++++++++
3 files changed, 72 insertions(+), 188 deletions(-)
delete mode 100644 src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java
diff --git a/src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java b/src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java
deleted file mode 100644
index 82aaf200ba1..00000000000
--- a/src/main/java/spoon/reflect/visitor/filter/SubInheritanceHierarchyFunction.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * 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.filter;
-
-import spoon.reflect.declaration.CtElement;
-import spoon.reflect.declaration.CtType;
-import spoon.reflect.declaration.CtTypeInformation;
-import spoon.reflect.reference.CtTypeReference;
-import spoon.reflect.visitor.chain.CtConsumableFunction;
-import spoon.reflect.visitor.chain.CtConsumer;
-import spoon.reflect.visitor.chain.CtQuery;
-import spoon.reflect.visitor.chain.CtQueryAware;
-import spoon.support.visitor.SubInheritanceHierarchyResolver;
-
-/**
- * Expects a {@link CtTypeInformation} as input
- * and produces all sub classes and sub interfaces recursively.
- * The output is produced in arbitrary order.
- */
-public class SubInheritanceHierarchyFunction implements CtConsumableFunction, CtQueryAware {
-
- private boolean includingSelf = false;
- private boolean includingInterfaces = true;
- private CtQuery query;
- private boolean failOnClassNotFound = false;
- private boolean returnTypeReferences = false;
-
- /**
- * The mapping function created using this constructor
- * will visit each super class and super interface
- * following super hierarchy. It can happen
- * that some interfaces will be visited more then once
- * if they are in super inheritance hierarchy more then once.
- * Use second constructor if you want to visit each interface only once.
- */
- public SubInheritanceHierarchyFunction() {
- }
-
- /**
- * @param includingSelf if true then input element is sent to output too. By default it is false.
- */
- public SubInheritanceHierarchyFunction includingSelf(boolean includingSelf) {
- this.includingSelf = includingSelf;
- return this;
- }
-
- /**
- * @param includingInterfaces if false then interfaces are not visited - only super classes. By default it is true.
- */
- public SubInheritanceHierarchyFunction includingInterfaces(boolean includingInterfaces) {
- this.includingInterfaces = includingInterfaces;
- return this;
- }
-
- /**
- * configures whether {@link CtType} or {@link CtTypeReference} instances are returned by this mapping function
- * @param returnTypeReferences if true then {@link CtTypeReference} instances are returned by this mapping function
- * @return this to support fluent API
- */
- public SubInheritanceHierarchyFunction returnTypeReferences(boolean returnTypeReferences) {
- this.returnTypeReferences = returnTypeReferences;
- return this;
- }
-
- /**
- * @param failOnClassNotFound sets whether processing should throw an exception if class is missing in noClassPath mode
- */
- public SubInheritanceHierarchyFunction failOnClassNotFound(boolean failOnClassNotFound) {
- this.failOnClassNotFound = failOnClassNotFound;
- return this;
- }
-
- @Override
- public void apply(CtTypeInformation input, final CtConsumer outputConsumer) {
- final SubInheritanceHierarchyResolver fnc = new SubInheritanceHierarchyResolver(input)
- .failOnClassNotFound(failOnClassNotFound)
- .includingInterfaces(includingInterfaces)
- .returnTypeReferences(returnTypeReferences);
- if (includingSelf) {
- if (returnTypeReferences) {
- if (input instanceof CtTypeReference) {
- outputConsumer.accept(input);
- } else {
- outputConsumer.accept(((CtType>) input).getReference());
- }
- } else {
- if (input instanceof CtTypeReference) {
- outputConsumer.accept(((CtTypeReference>) input).getTypeDeclaration());
- } else {
- outputConsumer.accept(((CtType>) input));
- }
- }
- }
- fnc.forEachSubTypeInPackage(((CtElement) input).getFactory().getModel().getRootPackage(), new CtConsumer() {
- @Override
- public void accept(CtTypeInformation typeInfo) {
- outputConsumer.accept(typeInfo);
- if (query.isTerminated()) {
- fnc.terminate();
- }
- }
- });
- }
-
-
- @Override
- public void setQuery(CtQuery query) {
- this.query = query;
- }
-}
diff --git a/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
index a57bd08da14..4e4cb3a16d6 100644
--- a/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
+++ b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
@@ -44,9 +44,7 @@
/**
* Expects a {@link CtPackage} as input
- * and produces all sub classes and sub interfaces, which extends or implements super type(s) provided in constructor and stored as `targetSuperTypes`.
- * The input `targetSuperTypes` itself are not returned as result.
- * The output is produced in arbitrary order.
+ * and upon calls to forEachSubTypeInPackage produces all sub classes and sub interfaces, which extends or implements super type(s) provided in constructor and stored as `targetSuperTypes`.
*
* The repeated processing of this mapping function on the same input returns only newly found sub types.
* The instance of {@link SubInheritanceHierarchyResolver} returns found sub types only once.
@@ -54,6 +52,9 @@
* Create and use new instance of {@link SubInheritanceHierarchyResolver} if you need to scan the subtype hierarchy again.
*/
public class SubInheritanceHierarchyResolver {
+
+ CtPackage inputPackage;
+
private boolean includingInterfaces = true;
/**
* Set of qualified names of all super types whose sub types we are searching for.
@@ -66,22 +67,9 @@ public class SubInheritanceHierarchyResolver {
*/
boolean hasSuperInterface = false;
private boolean failOnClassNotFound = false;
- private boolean returnTypeReferences = false;
- private boolean terminated = false;
-
- /**
- * @param superType target super type
- */
- public SubInheritanceHierarchyResolver(CtTypeInformation superType) {
- addSuperType(superType);
- }
-
- /**
- * @return set of qualified names of types, of all super types and all found sub types
- */
- public Set getHierarchyTypesQualifiedNames() {
- return targetSuperTypes;
+ public SubInheritanceHierarchyResolver(CtPackage input) {
+ inputPackage = input;
}
/**
@@ -106,16 +94,6 @@ public SubInheritanceHierarchyResolver includingInterfaces(boolean includingInte
return this;
}
- /**
- * configures whether {@link CtType} or {@link CtTypeReference} instances are returned by this mapping function
- * @param returnTypeReferences if true then {@link CtTypeReference} instances are returned by this mapping function
- * @return this to support fluent API
- */
- public SubInheritanceHierarchyResolver returnTypeReferences(boolean returnTypeReferences) {
- this.returnTypeReferences = returnTypeReferences;
- return this;
- }
-
/**
* @param failOnClassNotFound sets whether processing should throw an exception if class is missing in noClassPath mode
*/
@@ -125,31 +103,15 @@ public SubInheritanceHierarchyResolver failOnClassNotFound(boolean failOnClassNo
}
/**
- * Terminates the evaluation.
- */
- public void terminate() {
- terminated = true;
- }
-
- /**
- * @return true if the evaluation has been terminated.
- */
- public boolean isTerminated() {
- return terminated;
- }
-
- /**
- * Calls `outputConsumer.apply(subType)` for each sub type found in `inputPackage`.
+ * Calls `outputConsumer.apply(subType)` for each sub type of the targetSuperTypes that are found in `inputPackage`.
* Each sub type is returned only once.
- * If {@link #forEachSubTypeInPackage(CtPackage, CtConsumer)} is called again with same input and configuration
- * on the same instance of {@link SubInheritanceHierarchyResolver} and on the same model,
- * then it returns no sub types, because there are no NEW subtypes.
* It makes sense to call this method again for example after new super types are added
* by {@link #addSuperType(CtTypeInformation)}.
- * @param inputPackage the package where sub types are searched for
+ *
+ * If this method is called again with same input and configuration, nothing in sent to outputConsumer
* @param outputConsumer the consumer for found sub types
*/
- public void forEachSubTypeInPackage(CtPackage inputPackage, final CtConsumer outputConsumer) {
+ public void forEachSubTypeInPackage(final CtConsumer outputConsumer) {
/*
* Set of qualified names of all visited types, independent on whether they are sub types or not.
*/
@@ -189,10 +151,6 @@ public void forEachSubTypeInPackage(CtPackage inpu
.setListener(new CtScannerListener() {
@Override
public ScanningMode enter(CtElement element) {
- if (terminated) {
- q.terminate();
- return SKIP_ALL;
- }
CtTypeReference> typeRef = (CtTypeReference>) element;
String qName = typeRef.getQualifiedName();
if (targetSuperTypes.contains(qName)) {
@@ -205,7 +163,10 @@ public ScanningMode enter(CtElement element) {
/*
* Send them to outputConsumer and add then as targetSuperTypes too, to perform faster with detection of next sub types.
*/
- sendResult(typeRef, outputConsumer);
+ if (!targetSuperTypes.contains(typeRef.getQualifiedName())) {
+ targetSuperTypes.add(typeRef.getQualifiedName());
+ outputConsumer.accept((T) typeRef.getTypeDeclaration());
+ }
}
//we do not have to go deeper into super inheritance hierarchy. Skip visiting of further super types
//but continue visiting of siblings (do not terminate query)
@@ -249,17 +210,6 @@ public void accept(CtType> type) {
});
}
- @SuppressWarnings("unchecked")
- private void sendResult(CtTypeReference> typeRef, CtConsumer outputConsumer) {
- if (targetSuperTypes.add(typeRef.getQualifiedName())) {
- if (returnTypeReferences) {
- outputConsumer.accept((T) typeRef);
- } else {
- outputConsumer.accept((T) typeRef.getTypeDeclaration());
- }
- }
- }
-
/**
* accept all {@link CtType} excluding {@link CtTypeParameter}
*/
diff --git a/src/test/java/spoon/test/filters/FilterTest.java b/src/test/java/spoon/test/filters/FilterTest.java
index 544e984d12a..3c55fa8d051 100644
--- a/src/test/java/spoon/test/filters/FilterTest.java
+++ b/src/test/java/spoon/test/filters/FilterTest.java
@@ -39,6 +39,7 @@
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtType;
+import spoon.reflect.declaration.CtTypeInformation;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.factory.Factory;
@@ -72,6 +73,7 @@
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.comparator.DeepRepresentationComparator;
import spoon.support.reflect.declaration.CtMethodImpl;
+import spoon.support.visitor.SubInheritanceHierarchyResolver;
import spoon.test.filters.testclasses.AbstractTostada;
import spoon.test.filters.testclasses.Antojito;
import spoon.test.filters.testclasses.FieldAccessFilterTacos;
@@ -1045,4 +1047,60 @@ public void exit(CtElement element) {
//contract: if enter is called and does not returns SKIP_ALL, then exit must be called too. Exceptions are ignored for now
assertEquals(context2.nrOfEnterRetTrue, context2.nrOfExit);
}
+
+ @Test
+ public void testSubInheritanceHierarchyResolver() throws Exception {
+ // contract; SubInheritanceHierarchyResolver supports finding subtypes in an incremental manner
+ final Launcher launcher = new Launcher();
+ launcher.setArgs(new String[] {"--output-type", "nooutput","--level","info" });
+ launcher.addInputResource("./src/test/java/spoon/test/filters/testclasses");
+ launcher.buildModel();
+
+ SubInheritanceHierarchyResolver resolver = new SubInheritanceHierarchyResolver(launcher.getModel().getRootPackage());
+
+ // contract: by default, nothing is sent to the consumer
+ resolver.forEachSubTypeInPackage(new CtConsumer() {
+ @Override
+ public void accept(CtTypeInformation ctTypeInformation) {
+ fail();
+ }
+ });
+
+ // we add a type
+ resolver.addSuperType(launcher.getFactory().Type().createReference(AbstractTostada.class));
+ class Counter { int counter =0;}
+ Counter c = new Counter();
+ resolver.forEachSubTypeInPackage(new CtConsumer() {
+ @Override
+ public void accept(CtTypeInformation ctTypeInformation) {
+ c.counter++;
+ }
+ });
+
+ // there are 5 subtypes of AbstractTostada
+ assertEquals(5, c.counter);
+
+ // we add a type already visited
+ resolver.addSuperType(launcher.getFactory().Type().createReference(Tostada.class));
+ // nothing is sent to the consumer
+ resolver.forEachSubTypeInPackage(new CtConsumer() {
+ @Override
+ public void accept(CtTypeInformation ctTypeInformation) {
+ fail();
+ }
+ });
+
+ // we add a new type
+ resolver.addSuperType(launcher.getFactory().Type().createReference(ITostada.class));
+ Counter c2 = new Counter();
+ resolver.forEachSubTypeInPackage(new CtConsumer() {
+ @Override
+ public void accept(CtTypeInformation ctTypeInformation) {
+ c2.counter++;
+ }
+ });
+
+ // only one subtype remains unvisited
+ assertEquals(1, c2.counter);
+ }
}
From c1bb67d78b8e69d629eb2f315dfdacf6b91e165e Mon Sep 17 00:00:00 2001
From: Martin Monperrus
Date: Thu, 18 May 2017 22:16:38 +0200
Subject: [PATCH 3/4] minor stuff
---
.../visitor/SubInheritanceHierarchyResolver.java | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
index 4e4cb3a16d6..ea4f981526a 100644
--- a/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
+++ b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
@@ -53,8 +53,10 @@
*/
public class SubInheritanceHierarchyResolver {
- CtPackage inputPackage;
+ /** where the subtypes will be looked for */
+ private CtPackage inputPackage;
+ /** whether interfaces are included in the result */
private boolean includingInterfaces = true;
/**
* Set of qualified names of all super types whose sub types we are searching for.
@@ -65,7 +67,8 @@ public class SubInheritanceHierarchyResolver {
* if true then we have to check if type is a subtype of superClass or superInterfaces too
* if false then it is enough to search in superClass hierarchy only (faster)
*/
- boolean hasSuperInterface = false;
+ private boolean hasSuperInterface = false;
+
private boolean failOnClassNotFound = false;
public SubInheritanceHierarchyResolver(CtPackage input) {
@@ -139,8 +142,6 @@ public void forEachSubTypeInPackage(final CtConsum
q.map(new SuperInheritanceHierarchyFunction()
//if there is any interface between `targetSuperTypes`, then we have to check superInterfaces too
.includingInterfaces(hasSuperInterface)
- //internally it works always with references
- .returnTypeReferences(true)
.failOnClassNotFound(failOnClassNotFound)
/*
* listen for types in super inheritance hierarchy
@@ -163,8 +164,8 @@ public ScanningMode enter(CtElement element) {
/*
* Send them to outputConsumer and add then as targetSuperTypes too, to perform faster with detection of next sub types.
*/
- if (!targetSuperTypes.contains(typeRef.getQualifiedName())) {
- targetSuperTypes.add(typeRef.getQualifiedName());
+ if (!targetSuperTypes.contains(qName)) {
+ targetSuperTypes.add(qName);
outputConsumer.accept((T) typeRef.getTypeDeclaration());
}
}
From d16c3b9931a18fe771628d763738e4a4048c00de Mon Sep 17 00:00:00 2001
From: Martin Monperrus
Date: Thu, 18 May 2017 22:31:47 +0200
Subject: [PATCH 4/4] minor stuff
---
.../visitor/SubInheritanceHierarchyResolver.java | 11 ++++++-----
src/test/java/spoon/test/filters/FilterTest.java | 1 +
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
index ea4f981526a..6abd297df2f 100644
--- a/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
+++ b/src/main/java/spoon/support/visitor/SubInheritanceHierarchyResolver.java
@@ -152,7 +152,7 @@ public void forEachSubTypeInPackage(final CtConsum
.setListener(new CtScannerListener() {
@Override
public ScanningMode enter(CtElement element) {
- CtTypeReference> typeRef = (CtTypeReference>) element;
+ final CtTypeReference> typeRef = (CtTypeReference>) element;
String qName = typeRef.getQualifiedName();
if (targetSuperTypes.contains(qName)) {
/*
@@ -160,13 +160,14 @@ public ScanningMode enter(CtElement element) {
* All `currentSubTypes` are sub types of searched super type
*/
while (currentSubTypes.size() > 0) {
- typeRef = currentSubTypes.pop();
+ final CtTypeReference> currentTypeRef = currentSubTypes.pop();
+ String currentQName = currentTypeRef.getQualifiedName();
/*
* Send them to outputConsumer and add then as targetSuperTypes too, to perform faster with detection of next sub types.
*/
- if (!targetSuperTypes.contains(qName)) {
- targetSuperTypes.add(qName);
- outputConsumer.accept((T) typeRef.getTypeDeclaration());
+ if (!targetSuperTypes.contains(currentQName)) {
+ targetSuperTypes.add(currentQName);
+ outputConsumer.accept((T) currentTypeRef.getTypeDeclaration());
}
}
//we do not have to go deeper into super inheritance hierarchy. Skip visiting of further super types
diff --git a/src/test/java/spoon/test/filters/FilterTest.java b/src/test/java/spoon/test/filters/FilterTest.java
index 3c55fa8d051..aa20ccd26ed 100644
--- a/src/test/java/spoon/test/filters/FilterTest.java
+++ b/src/test/java/spoon/test/filters/FilterTest.java
@@ -1097,6 +1097,7 @@ public void accept(CtTypeInformation ctTypeInformation) {
@Override
public void accept(CtTypeInformation ctTypeInformation) {
c2.counter++;
+ assertEquals("spoon.test.filters.testclasses.Tacos", ctTypeInformation.getQualifiedName());
}
});