use of spoon.reflect.visitor.chain.CtQuery in project spoon by INRIA.
the class AllMethodsSameSignatureFunction method forEachOverridenMethod.
/**
* calls outputConsumer for each method which is overridden by 'thisMethod' in scope of `ctc`.
* There is assured that each method is returned only once.
*
* @param ctc - class typing context whose scope is searched for overridden methods
* @param thisMethod - the
* @param distintTypesSet set of qualified names of types which were already visited
* @param outputConsumer result handling consumer
*/
private void forEachOverridenMethod(final ClassTypingContext ctc, final CtMethod<?> thisMethod, Set<String> distintTypesSet, final CtConsumer<CtMethod<?>> outputConsumer) {
final CtQuery q = ctc.getAdaptationScope().map(new AllTypeMembersFunction(CtMethod.class).distinctSet(distintTypesSet));
q.forEach(new CtConsumer<CtMethod<?>>() {
@Override
public void accept(CtMethod<?> thatMethod) {
if (thisMethod == thatMethod) {
// do not return scope method
return;
}
/*
* note: we are in super inheritance hierarchy of type declaring input `method`, so we do not have to check isSubTypeOf.
* Check for isSubSignature is enough
*/
if (ctc.isSubSignature(thisMethod, thatMethod)) {
outputConsumer.accept(thatMethod);
if (query.isTerminated()) {
q.terminate();
}
}
}
});
}
use of spoon.reflect.visitor.chain.CtQuery in project spoon by INRIA.
the class AllTypeMembersFunction method apply.
@Override
public void apply(CtTypeInformation input, final CtConsumer<Object> outputConsumer) {
final CtQuery q = ((CtQueryable) input).map(new SuperInheritanceHierarchyFunction(distinctSet == null ? new HashSet<String>() : distinctSet).includingSelf(true));
q.forEach(new CtConsumer<CtType<?>>() {
@Override
public void accept(CtType<?> type) {
for (CtTypeMember typeMember : type.getTypeMembers()) {
if (memberClass == null || memberClass.isInstance(typeMember)) {
outputConsumer.accept(typeMember);
}
if (query.isTerminated()) {
q.terminate();
}
}
}
});
}
use of spoon.reflect.visitor.chain.CtQuery in project spoon by INRIA.
the class SubInheritanceHierarchyResolver method forEachSubTypeInPackage.
/**
* Calls `outputConsumer.apply(subType)` for each sub type of the targetSuperTypes that are found in `inputPackage`.
* Each sub type is returned only once.
* It makes sense to call this method again for example after new super types are added
* by {@link #addSuperType(CtTypeInformation)}.
*
* 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 <T extends CtType<?>> void forEachSubTypeInPackage(final CtConsumer<T> outputConsumer) {
/*
* Set of qualified names of all visited types, independent on whether they are sub types or not.
*/
final Set<String> 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<CtTypeReference<?>> 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().includingInterfaces(hasSuperInterface).failOnClassNotFound(failOnClassNotFound).setListener(new CtScannerListener() {
@Override
public ScanningMode enter(CtElement element) {
final 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) {
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(currentQName)) {
targetSuperTypes.add(currentQName);
outputConsumer.accept((T) currentTypeRef.getTypeDeclaration());
}
}
// 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<CtType<?>>() {
@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
}
});
}
use of spoon.reflect.visitor.chain.CtQuery in project spoon by INRIA.
the class FilterTest method testElementMapFunctionOtherContracts.
@Test
public void testElementMapFunctionOtherContracts() throws Exception {
// contract: when a function returns an array, all non-null values are sent to the next step
final Launcher launcher = new Launcher();
CtQuery q = launcher.getFactory().Query().createQuery().map((String s) -> new String[] { "a", null, s });
List<String> list = q.setInput(null).list();
assertEquals(0, list.size());
list = q.setInput("c").list();
assertEquals(2, list.size());
assertEquals("a", list.get(0));
assertEquals("c", list.get(1));
// contract: the input is stored, and we can evaluate the same query several times
// using "c" as input
list = q.list();
assertEquals(2, list.size());
assertEquals("a", list.get(0));
assertEquals("c", list.get(1));
// contract: when input is null then the query function is not called at all.
CtQuery q2 = launcher.getFactory().Query().createQuery().map((String s) -> {
throw new AssertionError();
});
assertEquals(0, q2.setInput(null).list().size());
}
use of spoon.reflect.visitor.chain.CtQuery in project spoon by INRIA.
the class FilterTest method testReuseOfQuery.
@Test
public void testReuseOfQuery() throws Exception {
// contract: a query created from an existing element can be reused on other inputs
final Launcher launcher = new Launcher();
launcher.setArgs(new String[] { "--output-type", "nooutput", "--level", "info" });
launcher.addInputResource("./src/test/java/spoon/test/filters/testclasses");
launcher.run();
CtClass<?> cls = launcher.getFactory().Class().get(Tacos.class);
CtClass<?> cls2 = launcher.getFactory().Class().get(Tostada.class);
// by default the query starts with "cls" as input
CtQuery q = cls.map((CtClass c) -> c.getSimpleName());
// high-level assert
assertEquals(cls.getSimpleName(), q.list().get(0));
// low-level assert on implementation
assertEquals(1, ((CtQueryImpl) q).getInputs().size());
assertSame(cls, ((CtQueryImpl) q).getInputs().get(0));
// now changing the input of query to cls2
q.setInput(cls2);
// the input is still cls2
assertEquals(cls2.getSimpleName(), q.list().get(0));
assertEquals(1, ((CtQueryImpl) q).getInputs().size());
assertSame(cls2, ((CtQueryImpl) q).getInputs().get(0));
}
Aggregations