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()); } });