Search in sources :

Example 1 with ScanningMode

use of spoon.reflect.visitor.chain.ScanningMode 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
        }
    });
}
Also used : SuperInheritanceHierarchyFunction(spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction) SpoonException(spoon.SpoonException) CtElement(spoon.reflect.declaration.CtElement) CtQuery(spoon.reflect.visitor.chain.CtQuery) CtTypeInformation(spoon.reflect.declaration.CtTypeInformation) ArrayDeque(java.util.ArrayDeque) ScanningMode(spoon.reflect.visitor.chain.ScanningMode) CtType(spoon.reflect.declaration.CtType) CtTypeReference(spoon.reflect.reference.CtTypeReference) CtScannerFunction(spoon.reflect.visitor.filter.CtScannerFunction) CtScannerListener(spoon.reflect.visitor.chain.CtScannerListener) HashSet(java.util.HashSet)

Example 2 with ScanningMode

use of spoon.reflect.visitor.chain.ScanningMode in project spoon by INRIA.

the class EarlyTerminatingScanner method scan.

/*
	 * we cannot override scan(CtRole role, CtElement element) directly
	 * because some implementations needs scan(CtElement element), which must be called too
	 */
@Override
public void scan(CtElement element) {
    if (element == null || isTerminated()) {
        return;
    }
    if (listener == null) {
        // the listener is not defined
        // visit this element and may be children
        doScan(scannedRole, 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(scannedRole, 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.
    }
}
Also used : ScanningMode(spoon.reflect.visitor.chain.ScanningMode)

Example 3 with ScanningMode

use of spoon.reflect.visitor.chain.ScanningMode in project spoon by INRIA.

the class FilterTest method testCtScannerListener.

@Test
public void testCtScannerListener() throws Exception {
    // contract: CtScannerFunction can be subclassed and configured by a CtScannerListener
    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();
    class Context {

        long nrOfEnter = 0;

        long nrOfEnterRetTrue = 0;

        long nrOfExit = 0;

        long nrOfResults = 0;
    }
    Context context1 = new Context();
    // scan only packages until top level classes. Do not scan class internals
    List<CtElement> result1 = launcher.getFactory().getModel().map(new CtScannerFunction().setListener(new CtScannerListener() {

        @Override
        public ScanningMode enter(CtElement element) {
            context1.nrOfEnter++;
            if (element instanceof CtType) {
                return ScanningMode.SKIP_CHILDREN;
            }
            return ScanningMode.NORMAL;
        }

        @Override
        public void exit(CtElement element) {
            context1.nrOfExit++;
        }
    })).list();
    // check that test is visiting some nodes
    assertTrue(context1.nrOfEnter > 0);
    assertTrue(result1.size() > 0);
    // contract: if enter is called and returns SKIP_CHILDREN or NORMAL, then exit must be called too. Exceptions are ignored for now
    assertEquals(context1.nrOfEnter, context1.nrOfExit);
    Context context2 = new Context();
    Iterator iter = result1.iterator();
    // scan only from packages till top level classes. Do not scan class internals
    launcher.getFactory().getModel().map(new CtScannerFunction().setListener(new CtScannerListener() {

        int inClass = 0;

        @Override
        public ScanningMode enter(CtElement element) {
            context2.nrOfEnter++;
            if (inClass > 0) {
                // we are in class. skip this node and all children
                return ScanningMode.SKIP_ALL;
            }
            if (element instanceof CtType) {
                inClass++;
            }
            context2.nrOfEnterRetTrue++;
            return ScanningMode.NORMAL;
        }

        @Override
        public void exit(CtElement element) {
            context2.nrOfExit++;
            if (element instanceof CtType) {
                inClass--;
            }
            assertTrue(inClass == 0 || inClass == 1);
        }
    })).forEach(ele -> {
        context2.nrOfResults++;
        assertTrue("ele instanceof " + ele.getClass(), ele instanceof CtPackage || ele instanceof CtType || ele instanceof CtModule);
        // check that first and second query returned same results
        assertSame(ele, iter.next());
    });
    // check that test is visiting some nodes
    assertTrue(context2.nrOfEnter > 0);
    assertTrue(context2.nrOfEnter > context2.nrOfEnterRetTrue);
    assertEquals(result1.size(), context2.nrOfResults);
    // 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);
}
Also used : CtElement(spoon.reflect.declaration.CtElement) ScanningMode(spoon.reflect.visitor.chain.ScanningMode) CtModule(spoon.reflect.declaration.CtModule) CtType(spoon.reflect.declaration.CtType) CtScannerFunction(spoon.reflect.visitor.filter.CtScannerFunction) Iterator(java.util.Iterator) Launcher(spoon.Launcher) CtPackage(spoon.reflect.declaration.CtPackage) CtScannerListener(spoon.reflect.visitor.chain.CtScannerListener) Test(org.junit.Test)

Example 4 with ScanningMode

use of spoon.reflect.visitor.chain.ScanningMode in project spoon by INRIA.

the class SuperInheritanceHierarchyFunction method apply.

@Override
public void apply(CtTypeInformation input, CtConsumer<Object> outputConsumer) {
    CtTypeReference<?> typeRef;
    CtType<?> type;
    // detect whether input is a class or something else (e.g. interface)
    boolean isClass;
    if (input instanceof CtType) {
        type = (CtType<?>) input;
        typeRef = type.getReference();
    } else {
        typeRef = (CtTypeReference<?>) input;
        try {
            type = typeRef.getTypeDeclaration();
        } catch (SpoonClassNotFoundException e) {
            if (typeRef.getFactory().getEnvironment().getNoClasspath() == false) {
                throw e;
            }
            type = null;
        }
    }
    // if the type is unknown, than we expect it is interface, otherwise we would visit java.lang.Object too, even for interfaces
    isClass = type == null ? false : (type instanceof CtClass);
    if (isClass == false && includingInterfaces == false) {
        // the input is interface, but this scanner should visit only interfaces. Finish
        return;
    }
    ScanningMode mode = enter(typeRef, isClass);
    if (mode == SKIP_ALL) {
        // listener decided to not visit that input. Finish
        return;
    }
    if (includingSelf) {
        sendResult(typeRef, outputConsumer);
        if (query.isTerminated()) {
            mode = SKIP_CHILDREN;
        }
    }
    if (mode == NORMAL) {
        if (isClass == false) {
            visitSuperInterfaces(typeRef, outputConsumer);
        } else {
            // call visitSuperClasses only for input of type class. The contract of visitSuperClasses requires that
            visitSuperClasses(typeRef, outputConsumer, includingInterfaces);
        }
    }
    exit(typeRef, isClass);
}
Also used : CtClass(spoon.reflect.declaration.CtClass) CtType(spoon.reflect.declaration.CtType) SpoonClassNotFoundException(spoon.support.SpoonClassNotFoundException) ScanningMode(spoon.reflect.visitor.chain.ScanningMode)

Example 5 with ScanningMode

use of spoon.reflect.visitor.chain.ScanningMode in project spoon by INRIA.

the class SuperInheritanceHierarchyFunction method visitSuperClasses.

/**
 * calls `outputConsumer.accept(superClass)` for all super classes of superType.
 *
 * @param superTypeRef the reference to a class. This method is called only for classes. Never for interface
 * @param includingInterfaces if true then all superInterfaces of each type are sent to `outputConsumer` too.
 */
protected void visitSuperClasses(CtTypeReference<?> superTypeRef, CtConsumer<Object> outputConsumer, boolean includingInterfaces) {
    if (Object.class.getName().equals(superTypeRef.getQualifiedName())) {
        // java.lang.Object has no interface or super classes
        return;
    }
    if (includingInterfaces) {
        visitSuperInterfaces(superTypeRef, outputConsumer);
        if (query.isTerminated()) {
            return;
        }
    }
    CtTypeReference<?> superClassRef = superTypeRef.getSuperclass();
    if (superClassRef == null) {
        // only CtClasses extend object,
        // this method is called only for classes (not for interfaces) so we know we can visit java.lang.Object now too
        superClassRef = superTypeRef.getFactory().Type().OBJECT;
    }
    ScanningMode mode = enter(superClassRef, true);
    if (mode == SKIP_ALL) {
        return;
    }
    sendResult(superClassRef, outputConsumer);
    if (mode == NORMAL && query.isTerminated() == false) {
        visitSuperClasses(superClassRef, outputConsumer, includingInterfaces);
    }
    exit(superClassRef, true);
}
Also used : ScanningMode(spoon.reflect.visitor.chain.ScanningMode)

Aggregations

ScanningMode (spoon.reflect.visitor.chain.ScanningMode)7 CtType (spoon.reflect.declaration.CtType)4 CtElement (spoon.reflect.declaration.CtElement)3 CtTypeReference (spoon.reflect.reference.CtTypeReference)3 CtScannerListener (spoon.reflect.visitor.chain.CtScannerListener)3 HashSet (java.util.HashSet)2 Test (org.junit.Test)2 Launcher (spoon.Launcher)2 SpoonException (spoon.SpoonException)2 CtClass (spoon.reflect.declaration.CtClass)2 SpoonClassNotFoundException (spoon.support.SpoonClassNotFoundException)2 File (java.io.File)1 FileReader (java.io.FileReader)1 IOException (java.io.IOException)1 ArrayDeque (java.util.ArrayDeque)1 ArrayList (java.util.ArrayList)1 Arrays (java.util.Arrays)1 Collection (java.util.Collection)1 Iterator (java.util.Iterator)1 List (java.util.List)1