Search in sources :

Example 1 with CtScannerFunction

use of spoon.reflect.visitor.filter.CtScannerFunction 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 CtScannerFunction

use of spoon.reflect.visitor.filter.CtScannerFunction 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)

Aggregations

CtElement (spoon.reflect.declaration.CtElement)2 CtType (spoon.reflect.declaration.CtType)2 CtScannerListener (spoon.reflect.visitor.chain.CtScannerListener)2 ScanningMode (spoon.reflect.visitor.chain.ScanningMode)2 CtScannerFunction (spoon.reflect.visitor.filter.CtScannerFunction)2 ArrayDeque (java.util.ArrayDeque)1 HashSet (java.util.HashSet)1 Iterator (java.util.Iterator)1 Test (org.junit.Test)1 Launcher (spoon.Launcher)1 SpoonException (spoon.SpoonException)1 CtModule (spoon.reflect.declaration.CtModule)1 CtPackage (spoon.reflect.declaration.CtPackage)1 CtTypeInformation (spoon.reflect.declaration.CtTypeInformation)1 CtTypeReference (spoon.reflect.reference.CtTypeReference)1 CtQuery (spoon.reflect.visitor.chain.CtQuery)1 SuperInheritanceHierarchyFunction (spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction)1