Search in sources :

Example 6 with CtInterface

use of spoon.reflect.declaration.CtInterface in project spoon by INRIA.

the class FilterTest method testInvalidQueryStep.

@Test
public void testInvalidQueryStep() throws Exception {
    // contract: with default policy an exception is thrown is the input type of a query step
    // does not correspond to the output type of the previous step
    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();
    try {
        launcher.getFactory().Package().getRootPackage().filterChildren((CtClass<?> c) -> {
            return true;
        }).name("step1").map((CtMethod<?> m) -> m).name("invalidStep2").forEach((CtInterface<?> c) -> {
            fail();
        });
        fail();
    } catch (ClassCastException e) {
        assertTrue(e.getMessage().indexOf("spoon.support.reflect.declaration.CtClassImpl cannot be cast to spoon.reflect.declaration.CtMethod") >= 0);
    }
}
Also used : CtClass(spoon.reflect.declaration.CtClass) CtInterface(spoon.reflect.declaration.CtInterface) Launcher(spoon.Launcher) CtMethod(spoon.reflect.declaration.CtMethod) Test(org.junit.Test)

Example 7 with CtInterface

use of spoon.reflect.declaration.CtInterface in project spoon by INRIA.

the class SpoonArchitectureEnforcerTest method testFactorySubFactory.

@Test
public void testFactorySubFactory() throws Exception {
    // contract:: all subfactory methods must also be in the main factory
    // this is very important for usability and discoverability
    final Launcher launcher = new Launcher();
    launcher.addInputResource("./src/main/java/spoon/reflect/factory");
    class SanityCheck {

        int val = 0;
    }
    ;
    SanityCheck sanityCheck = new SanityCheck();
    launcher.addProcessor(new AbstractManualProcessor() {

        @Override
        public void process() {
            CtType factoryImpl = getFactory().Interface().get(Factory.class);
            CtPackage factoryPackage = getFactory().Package().getOrCreate("spoon.reflect.factory");
            CtInterface itf = getFactory().Interface().create("MegaFactoryItf");
            CtClass impl = getFactory().Class().create("MegaFactory");
            for (CtType<?> t : factoryPackage.getTypes()) {
                // 
                if (t.getSimpleName().startsWith("Mega"))
                    continue;
                for (CtMethod<?> m : t.getMethods()) {
                    // we check only public methods
                    if (m.hasModifier(ModifierKind.PUBLIC) == false)
                        continue;
                    // we only consider factory methods
                    if (!m.getSimpleName().startsWith("create"))
                        continue;
                    // too generic, what should we create??
                    if (m.getSimpleName().equals("create")) {
                        String simpleNameType = m.getType().getSimpleName().replace("Ct", "");
                        CtMethod method = m.clone();
                        method.setSimpleName("create" + simpleNameType);
                        assertTrue(method.getSignature() + " (from " + t.getQualifiedName() + ") is not present in the main factory", factoryImpl.hasMethod(method));
                        continue;
                    }
                    // too generic, is it a fieldref? an execref? etc
                    if (m.getSimpleName().equals("createReference"))
                        continue;
                    if (m.getModifiers().contains(ModifierKind.ABSTRACT))
                        continue;
                    sanityCheck.val++;
                    // the core assertion
                    assertTrue(m.getSignature() + " is not present in the main factory", factoryImpl.hasMethod(m));
                }
            }
        }
    });
    launcher.run();
    assertTrue(sanityCheck.val > 100);
}
Also used : CtInterface(spoon.reflect.declaration.CtInterface) CtClass(spoon.reflect.declaration.CtClass) AbstractManualProcessor(spoon.processing.AbstractManualProcessor) CtType(spoon.reflect.declaration.CtType) Launcher(spoon.Launcher) Factory(spoon.reflect.factory.Factory) CtPackage(spoon.reflect.declaration.CtPackage) CtMethod(spoon.reflect.declaration.CtMethod) Test(org.junit.Test)

Example 8 with CtInterface

use of spoon.reflect.declaration.CtInterface in project spoon by INRIA.

the class JDTCommentBuilder method insertCommentInAST.

/**
 * Inserts the comment into the AST.
 * @param comment the comment to insert
 */
private void insertCommentInAST(final CtComment comment) {
    CtElement commentParent = findCommentParent(comment);
    if (commentParent == null) {
        File file = spoonUnit.getFile();
        if (file != null && file.getName().equals(DefaultJavaPrettyPrinter.JAVA_PACKAGE_DECLARATION)) {
            spoonUnit.getDeclaredPackage().addComment(comment);
        } else if (file != null && file.getName().equals(DefaultJavaPrettyPrinter.JAVA_MODULE_DECLARATION)) {
            spoonUnit.getDeclaredModule().addComment(comment);
        } else {
            comment.setCommentType(CtComment.CommentType.FILE);
            addCommentToNear(comment, new ArrayList<CtElement>(spoonUnit.getDeclaredTypes()));
        }
        return;
    }
    // visitor that inserts the comment in the element
    CtInheritanceScanner insertionVisitor = new CtInheritanceScanner() {

        private boolean isScanned = false;

        @Override
        public void scan(CtElement e) {
            if (e == null) {
                return;
            }
            // Do not visit the AST, only the first element
            if (!isScanned) {
                isScanned = true;
                if (e.getPosition().getSourceStart() == comment.getPosition().getSourceStart()) {
                    e.addComment(comment);
                    return;
                }
                super.scan(e);
            }
        }

        @Override
        public <R> void visitCtStatementList(CtStatementList e) {
            addCommentToNear(comment, new ArrayList<CtElement>(e.getStatements()));
            try {
                comment.getParent();
            } catch (ParentNotInitializedException ex) {
                e.addStatement(comment);
            }
        }

        @Override
        public <T> void visitCtMethod(CtMethod<T> e) {
            e.addComment(comment);
        }

        @Override
        public <T> void visitCtConstructor(CtConstructor<T> e) {
            e.addComment(comment);
        }

        @Override
        public <T> void visitCtConditional(CtConditional<T> e) {
            List<CtElement> elements = new ArrayList<>();
            elements.add(e.getElseExpression());
            elements.add(e.getThenExpression());
            elements.add(e.getCondition());
            addCommentToNear(comment, elements);
        }

        @Override
        public <T> void visitCtBinaryOperator(CtBinaryOperator<T> e) {
            List<CtElement> elements = new ArrayList<>();
            elements.add(e.getLeftHandOperand());
            elements.add(e.getRightHandOperand());
            addCommentToNear(comment, elements);
        }

        @Override
        public <T> void visitCtClass(CtClass<T> e) {
            if (comment.getPosition().getLine() <= e.getPosition().getLine()) {
                e.addComment(comment);
                return;
            }
            final List<CtElement> elements = new ArrayList<>();
            for (CtTypeMember typeMember : e.getTypeMembers()) {
                if (typeMember instanceof CtField || typeMember instanceof CtMethod || typeMember instanceof CtConstructor) {
                    elements.add(typeMember);
                }
            }
            addCommentToNear(comment, elements);
            try {
                comment.getParent();
            } catch (ParentNotInitializedException ex) {
                e.addComment(comment);
            }
        }

        @Override
        public <T> void visitCtInterface(CtInterface<T> e) {
            final List<CtElement> elements = new ArrayList<>();
            for (CtTypeMember typeMember : e.getTypeMembers()) {
                if (typeMember instanceof CtField || typeMember instanceof CtMethod) {
                    elements.add(typeMember);
                }
            }
            addCommentToNear(comment, elements);
            try {
                comment.getParent();
            } catch (ParentNotInitializedException ex) {
                e.addComment(comment);
            }
        }

        @Override
        public <T> void visitCtField(CtField<T> e) {
            e.addComment(comment);
        }

        @Override
        public <E> void visitCtSwitch(CtSwitch<E> e) {
            List<CtCase<? super E>> cases = e.getCases();
            CtCase previous = null;
            for (int i = 0; i < cases.size(); i++) {
                CtCase<? super E> ctCase = cases.get(i);
                if (previous == null) {
                    if (comment.getPosition().getSourceStart() < ctCase.getPosition().getSourceStart() && e.getPosition().getSourceStart() < comment.getPosition().getSourceStart()) {
                        ctCase.addComment(comment);
                        return;
                    }
                } else {
                    if (previous.getPosition().getSourceEnd() < comment.getPosition().getSourceStart() && ctCase.getPosition().getSourceStart() > comment.getPosition().getSourceStart()) {
                        addCommentToNear(comment, new ArrayList<CtElement>(previous.getStatements()));
                        try {
                            comment.getParent();
                        } catch (ParentNotInitializedException ex) {
                            previous.addStatement(comment);
                        }
                        return;
                    }
                }
                previous = ctCase;
            }
            if (previous.getPosition().getSourceEnd() < comment.getPosition().getSourceStart()) {
                addCommentToNear(comment, new ArrayList<CtElement>(previous.getStatements()));
                try {
                    comment.getParent();
                } catch (ParentNotInitializedException ex) {
                    previous.addStatement(comment);
                }
                return;
            }
            try {
                comment.getParent();
            } catch (ParentNotInitializedException ex) {
                e.addComment(comment);
            }
        }

        @Override
        public void visitCtIf(CtIf e) {
            if (!(e.getThenStatement() instanceof CtBlock)) {
                if (comment.getPosition().getSourceEnd() <= e.getThenStatement().getPosition().getSourceStart()) {
                    e.getThenStatement().addComment(comment);
                    return;
                }
            }
            if (e.getElseStatement() != null) {
                SourcePosition thenPosition = e.getThenStatement().getPosition() == null ? ((CtBlock) e.getThenStatement()).getStatement(0).getPosition() : e.getThenStatement().getPosition();
                SourcePosition elsePosition = e.getElseStatement().getPosition() == null ? ((CtBlock) e.getElseStatement()).getStatement(0).getPosition() : e.getElseStatement().getPosition();
                if (comment.getPosition().getSourceStart() > thenPosition.getSourceEnd() && comment.getPosition().getSourceEnd() < elsePosition.getSourceStart()) {
                    e.getElseStatement().addComment(comment);
                }
            }
            try {
                comment.getParent();
            } catch (ParentNotInitializedException ex) {
                e.addComment(comment);
            }
        }

        @Override
        public void scanCtStatement(CtStatement s) {
            if (!(s instanceof CtStatementList || s instanceof CtSwitch)) {
                s.addComment(comment);
            }
        }

        @Override
        public void visitCtAnonymousExecutable(CtAnonymousExecutable e) {
            e.addComment(comment);
        }

        @Override
        public <T> void visitCtNewArray(CtNewArray<T> e) {
            addCommentToNear(comment, new ArrayList<CtElement>(e.getElements()));
            try {
                comment.getParent();
            } catch (ParentNotInitializedException ex) {
                e.addComment(comment);
            }
        }

        @Override
        public <T> void visitCtParameter(CtParameter<T> e) {
            e.addComment(comment);
        }

        @Override
        public void visitCtCatch(CtCatch e) {
            if (comment.getPosition().getLine() <= e.getPosition().getLine()) {
                e.addComment(comment);
                return;
            }
        }

        @Override
        public void visitCtModule(CtModule module) {
            addCommentToNear(comment, new ArrayList<>(module.getModuleDirectives()));
        }
    };
    insertionVisitor.scan(commentParent);
    try {
        comment.getParent();
    } catch (ParentNotInitializedException e) {
        LOGGER.error(comment + " is not added into the AST", e);
    }
}
Also used : CtConditional(spoon.reflect.code.CtConditional) ParentNotInitializedException(spoon.reflect.declaration.ParentNotInitializedException) CtSwitch(spoon.reflect.code.CtSwitch) ArrayList(java.util.ArrayList) CtParameter(spoon.reflect.declaration.CtParameter) CtNewArray(spoon.reflect.code.CtNewArray) CtStatement(spoon.reflect.code.CtStatement) CtField(spoon.reflect.declaration.CtField) SourcePosition(spoon.reflect.cu.SourcePosition) CtInheritanceScanner(spoon.reflect.visitor.CtInheritanceScanner) CtStatementList(spoon.reflect.code.CtStatementList) CtInterface(spoon.reflect.declaration.CtInterface) CtBinaryOperator(spoon.reflect.code.CtBinaryOperator) CtElement(spoon.reflect.declaration.CtElement) CtIf(spoon.reflect.code.CtIf) CtConstructor(spoon.reflect.declaration.CtConstructor) CtModule(spoon.reflect.declaration.CtModule) CtClass(spoon.reflect.declaration.CtClass) CtTypeMember(spoon.reflect.declaration.CtTypeMember) CtBlock(spoon.reflect.code.CtBlock) CtCase(spoon.reflect.code.CtCase) CtCatch(spoon.reflect.code.CtCatch) File(java.io.File) CtAnonymousExecutable(spoon.reflect.declaration.CtAnonymousExecutable) CtMethod(spoon.reflect.declaration.CtMethod)

Example 9 with CtInterface

use of spoon.reflect.declaration.CtInterface in project spoon by INRIA.

the class CloneVisitorGenerator method process.

@Override
public void process() {
    final CtClass<Object> target = createCloneVisitor();
    final CtClass<Object> targetBuilder = createCloneBuilder();
    final Factory factory = target.getFactory();
    final CtTypeReference<Object> cloneBuilder = factory.Type().createReference("spoon.support.visitor.clone.CloneBuilder");
    final CtTypeAccess<Object> cloneBuilderType = factory.Code().createTypeAccess(cloneBuilder);
    final CtVariableAccess<Object> builderFieldAccess = factory.Code().createVariableRead(factory.Field().createReference(target.getReference(), cloneBuilder, "builder"), false);
    final CtVariableAccess<Object> tailorerFieldAccess = factory.Code().createVariableRead(factory.Field().createReference(target.getReference(), cloneBuilder, "tailorer"), false);
    final CtVariableAccess<Object> cloneHelperFieldAccess = factory.Code().createVariableRead(factory.Field().createReference(target.getReference(), cloneBuilder, "cloneHelper"), false);
    final CtFieldReference<Object> other = factory.Field().createReference((CtField) target.getField("other"));
    final CtVariableAccess otherRead = factory.Code().createVariableRead(other, true);
    new CtScanner() {

        private final List<String> internals = Collections.singletonList("CtCircularTypeReference");

        @Override
        public <T> void visitCtMethod(CtMethod<T> element) {
            if (!element.getSimpleName().startsWith("visitCt")) {
                return;
            }
            CtMethod<T> clone = element.clone();
            // Variables used by the visit method.
            final CtParameter<CtElement> ctParameter = (CtParameter<CtElement>) element.getParameters().get(0);
            final CtVariableAccess<CtElement> elementVarRead = factory.Code().createVariableRead(ctParameter.getReference(), false);
            final CtInvocation cloneBuilderInvocation = createCloneBuilderBuildInvocation(elementVarRead);
            final CtLocalVariable localCloningElement = createLocalCloningElement(ctParameter.getType(), createFactoryInvocation(elementVarRead));
            final CtVariableAccess localVarRead = factory.Code().createVariableRead(localCloningElement.getReference(), false);
            // Changes body of the cloned method.
            for (int i = 1; i < clone.getBody().getStatements().size() - 1; i++) {
                List<CtExpression> invArgs = ((CtInvocation) clone.getBody().getStatement(i)).getArguments();
                if (invArgs.size() <= 1) {
                    throw new RuntimeException("You forget the role argument in line " + i + " of method " + element.getSimpleName() + " from " + element.getDeclaringType().getQualifiedName());
                }
                final CtInvocation targetInvocation = (CtInvocation) invArgs.get(1);
                if ("getValue".equals(targetInvocation.getExecutable().getSimpleName()) && "CtLiteral".equals(targetInvocation.getExecutable().getDeclaringType().getSimpleName())) {
                    clone.getBody().getStatement(i--).delete();
                    continue;
                }
                // 
                clone.getBody().getStatement(i).replace(createSetter((CtInvocation) clone.getBody().getStatement(i), localVarRead));
            }
            // Delete enter and exit methods.
            clone.getBody().getStatement(0).delete();
            clone.getBody().getStatement(clone.getBody().getStatements().size() - 1).delete();
            // declaration of local variable
            clone.getBody().insertBegin(localCloningElement);
            // call to copy
            clone.getBody().insertEnd(createCloneBuilderCopyInvocation(elementVarRead, localVarRead));
            // call to tailor
            clone.getBody().insertEnd(createTailorerScanInvocation(elementVarRead, localVarRead));
            // final assignment
            clone.getBody().insertEnd(factory.Code().createVariableAssignment(other, false, localVarRead));
            // Add auto-generated comment.
            final CtComment comment = factory.Core().createComment();
            comment.setCommentType(CtComment.CommentType.INLINE);
            comment.setContent("auto-generated, see " + CloneVisitorGenerator.class.getName());
            clone.addComment(comment);
            target.addMethod(clone);
        }

        /**
         * Creates <code>anElement.setX(clone(anElement.getX()))</code>.
         *
         * @param scanInvocation <code>scan(anElement.getX())</code>.
         * @param elementVarRead <code>anElement</code>.
         */
        private CtInvocation<?> createSetter(CtInvocation scanInvocation, CtVariableAccess<CtElement> elementVarRead) {
            final CtInvocation<?> getter = (CtInvocation<?>) scanInvocation.getArguments().get(1);
            final String getterName = getter.getExecutable().getSimpleName();
            final CtExecutableReference<Object> setterRef = factory.Executable().createReference("void CtElement#set" + getterName.substring(3, getterName.length()) + "()");
            final CtExecutableReference<Object> cloneRef = factory.Executable().createReference("CtElement spoon.support.visitor.equals.CloneHelper#clone()");
            final CtInvocation<Object> cloneInv = factory.Code().createInvocation(null, cloneRef, getter);
            cloneInv.setTarget(cloneHelperFieldAccess);
            return factory.Code().createInvocation(elementVarRead, setterRef, cloneInv);
        }

        /**
         * Creates <code>CtElement anElement = CloneBuilder.build(builder, element, element.getFactory().Core().createElement())</code>.
         *
         * @param typeReference <code>CtElement</code>.
         * @param ctInvocation <code>CloneBuilder.build(builder, element, element.getFactory().Core().createElement())</code>.
         */
        private <T> CtLocalVariable<T> createLocalCloningElement(CtTypeReference<T> typeReference, CtInvocation<T> ctInvocation) {
            return factory.Code().createLocalVariable(typeReference, "a" + typeReference.getSimpleName(), ctInvocation);
        }

        /**
         * Creates <code>CloneBuilder.build(builder, element, element.getFactory().Core().createElement())</code>.
         *
         * @param elementAccess <code>element</code>.
         */
        private CtInvocation<CloneBuilder> createCloneBuilderBuildInvocation(CtVariableAccess<CtElement> elementAccess) {
            final CtExecutableReference<CloneBuilder> buildExecRef = factory.Executable().createReference("CloneBuilder CtElement#build()");
            return factory.Code().createInvocation(cloneBuilderType, buildExecRef, builderFieldAccess, elementAccess, createFactoryInvocation(elementAccess.clone()));
        }

        private CtInvocation<CloneBuilder> createCloneBuilderCopyInvocation(CtVariableAccess<CtElement> elementVarRead, CtVariableAccess<CtElement> elementVarRead2) {
            final CtExecutableReference<CloneBuilder> buildExecRef = factory.Executable().createReference("CloneBuilder #copy()");
            return factory.Code().createInvocation(builderFieldAccess, buildExecRef, elementVarRead, elementVarRead2);
        }

        private CtInvocation<CloneBuilder> createTailorerScanInvocation(CtVariableAccess elementVarRead, CtVariableAccess localVarRead) {
            final CtExecutableReference<CloneBuilder> buildExecRef = factory.Executable().createReference("CloneHelper #tailor()");
            return factory.Code().createInvocation(cloneHelperFieldAccess, buildExecRef, elementVarRead, localVarRead);
        }

        /**
         * Creates <code>element.getFactory().Core().createElement()</code>.
         *
         * @param elementAccess <code>element</code>.
         */
        private CtInvocation createFactoryInvocation(CtVariableAccess<CtElement> elementAccess) {
            final String typeName = elementAccess.getType().getSimpleName();
            // #getFactory()
            final CtInvocation<Object> getFactory = factory.Code().createInvocation(null, factory.Executable().createReference("Factory CtElement#getFactory()"));
            getFactory.setTarget(elementAccess);
            // Factory#Core() or Factory#Internal()
            final String factoryName = internals.contains(typeName) ? "Internal" : "Core";
            final CtInvocation<Object> coreFactory = factory.Code().createInvocation(getFactory, factory.Executable().createReference("CoreFactory Factory#" + factoryName + "()"));
            // CoreFactory#createElement()
            return factory.Code().createInvocation(coreFactory, factory.Executable().createReference("CoreFactory CtElement#create" + typeName.substring(2, typeName.length()) + "()"));
        }
    }.scan(getFactory().Class().get(CtScanner.class));
    new CtScanner() {

        private final List<String> excludesAST = // 
        Arrays.asList(// 
        "spoon.support.reflect.declaration.CtTypeInformationImpl", // 
        "spoon.support.reflect.code.CtAbstractInvocationImpl", // 
        "spoon.support.reflect.declaration.CtTypedElementImpl", // 
        "spoon.support.reflect.declaration.CtVariableImpl", // 
        "spoon.support.reflect.reference.CtActualTypeContainerImpl", // 
        "spoon.support.reflect.code.CtCFlowBreakImpl", // 
        "spoon.support.reflect.code.CtLabelledFlowBreakImpl", // 
        "spoon.support.reflect.declaration.CtCodeSnippetImpl", // 
        "spoon.support.reflect.declaration.CtFormalTypeDeclarerImpl", // 
        "spoon.support.reflect.declaration.CtGenericElementImpl", // 
        "spoon.support.reflect.reference.CtGenericElementReferenceImpl", // 
        "spoon.support.reflect.declaration.CtModifiableImpl", // 
        "spoon.support.reflect.declaration.CtMultiTypedElementImpl", "spoon.support.reflect.declaration.CtTypeMemberImpl", "spoon.support.reflect.code.CtRHSReceiverImpl", "spoon.support.reflect.declaration.CtShadowableImpl", "spoon.support.reflect.code.CtBodyHolderImpl", "spoon.support.reflect.declaration.CtModuleDirectiveImpl");

        private final List<String> excludesFields = Arrays.asList("factory", "elementValues", "target", "metadata");

        private final CtTypeReference<List> LIST_REFERENCE = factory.Type().createReference(List.class);

        private final CtTypeReference<Collection> COLLECTION_REFERENCE = factory.Type().createReference(Collection.class);

        private final CtTypeReference<Set> SET_REFERENCE = factory.Type().createReference(Set.class);

        private final CtTypeReference<CtElement> CTELEMENT_REFERENCE = factory.Type().createReference(CtElement.class);

        private final CtClass<?> GETTER_TEMPLATE_MATCHER_CLASS = factory.Class().get(GENERATING_CLONE_PACKAGE + ".GetterTemplateMatcher");

        private final CtClass<?> SETTER_TEMPLATE_MATCHER_CLASS = factory.Class().get(GENERATING_CLONE_PACKAGE + ".SetterTemplateMatcher");

        @Override
        public <T> void visitCtMethod(CtMethod<T> element) {
            if (!element.getSimpleName().startsWith("visitCt") && !element.getSimpleName().startsWith("scanCt")) {
                return;
            }
            if ("scanCtVisitable".equals(element.getSimpleName())) {
                return;
            }
            final String qualifiedNameOfImplClass = "spoon.support" + element.getParameters().get(0).getType().getQualifiedName().substring(5) + "Impl";
            if (excludesAST.contains(qualifiedNameOfImplClass)) {
                return;
            }
            final CtType<?> declaration = factory.Class().get(qualifiedNameOfImplClass);
            if (declaration == null) {
                throw new SpoonException(qualifiedNameOfImplClass + " doesn't have declaration in the source path for " + element.getSignature());
            }
            CtMethod<T> clone = element.clone();
            clone.getBody().getStatements().clear();
            for (CtField<?> ctField : declaration.getFields()) {
                if (excludesFields.contains(ctField.getSimpleName())) {
                    continue;
                }
                if (isConstantOrStatic(ctField)) {
                    continue;
                }
                if (isSubTypeOfCtElement(ctField.getType())) {
                    continue;
                }
                final CtMethod<?> setterOfField = getSetterOf(ctField);
                final CtInvocation<?> setterInvocation = createSetterInvocation(// 
                element.getParameters().get(0).getType(), // 
                setterOfField, createGetterInvocation(element.getParameters().get(0), getGetterOf(ctField)));
                final List<CtMethod<?>> methodsToAvoid = getCtMethodThrowUnsupportedOperation(setterOfField);
                if (methodsToAvoid.size() > 0) {
                    clone.getBody().addStatement(createProtectionToException(setterInvocation, methodsToAvoid));
                } else {
                    clone.getBody().addStatement(setterInvocation);
                }
            }
            if (clone.getBody().getStatements().size() > 0) {
                clone.getBody().insertEnd(createSuperInvocation(element));
                // Add auto-generated comment.
                final CtComment comment = factory.Core().createComment();
                comment.setCommentType(CtComment.CommentType.INLINE);
                comment.setContent("auto-generated, see " + CloneVisitorGenerator.class.getName());
                clone.addComment(comment);
                targetBuilder.addMethod(clone);
            }
        }

        /**
         * Creates <code>if (!(other instanceof CtX && other instanceof CtY && ..)) {}</code>.
         */
        private CtIf createProtectionToException(CtInvocation<?> setterInvocation, List<CtMethod<?>> methodsAvoid) {
            final CtIf anIf = factory.Core().createIf();
            anIf.setCondition(factory.Core().createUnaryOperator().setOperand(createBinaryConditions(methodsAvoid)).setKind(UnaryOperatorKind.NOT));
            anIf.setThenStatement(factory.Code().createCtBlock(setterInvocation));
            return anIf;
        }

        /**
         * Creates <code>condition && condition && ...</code>.
         *
         * @param methodsAvoid Methods to avoid.
         */
        private CtExpression<Object> createBinaryConditions(List<CtMethod<?>> methodsAvoid) {
            CtExpression<Object> left = null;
            CtExpression<Object> right;
            for (int i = 0; i < methodsAvoid.size(); i++) {
                final CtInterface<?> ctInterface = getInterfaceOf(methodsAvoid.get(i).getDeclaringType());
                if (i == 0) {
                    left = // 
                    factory.Code().createBinaryOperator(// 
                    otherRead, // 
                    factory.Code().createTypeAccess(ctInterface.getReference()), BinaryOperatorKind.INSTANCEOF);
                } else {
                    right = // 
                    factory.Code().createBinaryOperator(// 
                    otherRead, // 
                    factory.Code().createTypeAccess(ctInterface.getReference()), BinaryOperatorKind.INSTANCEOF);
                    left = factory.Code().createBinaryOperator(left, right, BinaryOperatorKind.OR);
                }
            }
            return left;
        }

        /**
         * Query to get all methods which throw an UnsupportedOperationException. We must avoid to call these methods during a clone process.
         */
        private List<CtMethod<?>> getCtMethodThrowUnsupportedOperation(CtMethod<?> method) {
            final List<CtMethod<?>> avoid = new ArrayList<>();
            final CtInterface<?> ctInterface = getInterfaceOf(method.getDeclaringType());
            if (ctInterface == null) {
                return avoid;
            }
            final CtMethod<?> declarationMethod = getMethodByCtMethod(ctInterface, method);
            for (CtMethod<?> ctMethod : Query.getElements(factory, new OverridingMethodFilter(declarationMethod))) {
                if (!avoidThrowUnsupportedOperationException(ctMethod)) {
                    avoid.add(ctMethod);
                }
            }
            return avoid;
        }

        /**
         * Check if the candidate method throw an UnsupportedOperationException.
         */
        private boolean avoidThrowUnsupportedOperationException(CtMethod<?> candidate) {
            if (candidate.getBody().getStatements().size() != 1) {
                return true;
            }
            if (!(candidate.getBody().getStatement(0) instanceof CtThrow)) {
                return true;
            }
            CtThrow ctThrow = candidate.getBody().getStatement(0);
            if (!(ctThrow.getThrownExpression() instanceof CtConstructorCall)) {
                return true;
            }
            final CtConstructorCall<? extends Throwable> thrownExpression = (CtConstructorCall<? extends Throwable>) ctThrow.getThrownExpression();
            if (!thrownExpression.getType().equals(factory.Type().createReference(UnsupportedOperationException.class))) {
                return true;
            }
            return false;
        }

        /**
         * Query to get a method from a CtMethod.
         */
        private CtMethod<?> getMethodByCtMethod(CtType<?> ctType, CtMethod<?> method) {
            for (CtMethod<?> ctMethod : ctType.getAllMethods()) {
                if (!method.getSimpleName().equals(ctMethod.getSimpleName())) {
                    continue;
                }
                boolean cont = method.getParameters().size() == ctMethod.getParameters().size();
                for (int i = 0; cont && i < method.getParameters().size(); i++) {
                    if (!method.getParameters().get(i).getType().equals(ctMethod.getParameters().get(i).getType())) {
                        cont = false;
                    }
                }
                if (cont) {
                    return ctMethod;
                }
            }
            throw new AssertionError("Can't find method " + method.getSignature() + " in the given interface " + ctType.getQualifiedName());
        }

        /**
         * Query to get the interface of the class.
         */
        private CtInterface<?> getInterfaceOf(CtType<?> declaringType) {
            final CtTypeReference<?>[] interfaces = declaringType.getSuperInterfaces().toArray(new CtTypeReference[declaringType.getSuperInterfaces().size()]);
            for (CtTypeReference<?> anInterface : interfaces) {
                if (anInterface.getSimpleName().equals(declaringType.getSimpleName().substring(0, declaringType.getSimpleName().length() - 4))) {
                    return (CtInterface<?>) anInterface.getDeclaration();
                }
            }
            throw new AssertionError("You should have the interface for the implementation " + declaringType.getQualifiedName());
        }

        /**
         * Creates <code>super.visitMethod(element)</code>.
         *
         * @param element <code>visitMethod</code>.
         */
        private <T> CtInvocation<T> createSuperInvocation(CtMethod<T> element) {
            return factory.Code().createInvocation(factory.Core().createSuperAccess(), element.getReference(), factory.Code().createVariableRead(element.getParameters().get(0).getReference(), false));
        }

        /**
         * Creates <code>((CtElement) other).setX(element.getX())</code>
         * or <code>((CtElement) other).setX(new Collection(element.getX()))</code>
         * if the field is a collection.
         *
         * @param type <code>CtElement</code>
         * @param setter <code>setX</code>.
         * @param getter <code>getX</code>.
         */
        private CtInvocation<?> createSetterInvocation(CtTypeReference<?> type, CtMethod<?> setter, CtInvocation<?> getter) {
            return factory.Code().createInvocation(otherRead.clone().addTypeCast(type), setter.getReference(), getter);
        }

        /**
         * Creates <code>element.getX()</code>.
         *
         * @param element <code>element</code>.
         * @param getter <code>getX</code>.
         */
        private CtInvocation<?> createGetterInvocation(CtParameter<?> element, CtMethod<?> getter) {
            return factory.Code().createInvocation(factory.Code().createVariableRead(element.getReference(), false), getter.getReference());
        }

        /**
         * Query to get the setter of given field.
         */
        private <T> CtMethod<?> getSetterOf(final CtField<T> ctField) {
            if (ctField.getType().equals(getFactory().createCtTypeReference(CtModifierHandler.class))) {
                return ctField.getDeclaringType().getMethodsByName("setModifiers").get(0);
            }
            // Search by name convention.
            for (CtMethod<?> ctMethod : ctField.getDeclaringType().getMethods()) {
                if (ctMethod.getSimpleName().startsWith("set") && ctMethod.getSimpleName().toLowerCase().contains(ctField.getSimpleName().toLowerCase())) {
                    if (ctMethod.getParameters().size() != 1) {
                        continue;
                    }
                    if (!ctMethod.getParameters().get(0).getType().equals(ctField.getType())) {
                        continue;
                    }
                    return ctMethod;
                }
            }
            SETTER_TEMPLATE_MATCHER_CLASS.getMethod("setElement", factory.Type().BOOLEAN_PRIMITIVE).getBody();
            final List<CtMethod> matchers = ctField.getDeclaringType().getElements(new TypeFilter<CtMethod>(CtMethod.class) {

                @Override
                public boolean matches(CtMethod element) {
                    final CtBlock body = element.getBody();
                    if (body.getStatements().size() != 3) {
                        return false;
                    }
                    if (body.getStatement(1) instanceof CtAssignment) {
                        final CtExpression assigned = ((CtAssignment) body.getStatement(1)).getAssigned();
                        if (!(assigned instanceof CtFieldAccess)) {
                            return false;
                        }
                        if (!((CtFieldAccess) assigned).getVariable().getSimpleName().equals(ctField.getSimpleName())) {
                            return false;
                        }
                    } else {
                        return false;
                    }
                    return true;
                }
            });
            if (matchers.size() != 1) {
                throw new SpoonException("Get more than one setter. Please make an more ingenious method to get setter method. " + matchers.size() + " " + ctField);
            }
            return matchers.get(0);
        }

        /**
         * Query to get the getter of the given field.
         */
        private <T> CtMethod<?> getGetterOf(CtField<T> ctField) {
            if (ctField.getType().equals(getFactory().createCtTypeReference(CtModifierHandler.class))) {
                return ctField.getDeclaringType().getMethod("getModifiers");
            }
            // Search by name convention.
            for (CtMethod<?> ctMethod : ctField.getDeclaringType().getMethods()) {
                if (// 
                (ctMethod.getSimpleName().startsWith("get") || ctMethod.getSimpleName().startsWith("is")) && ctMethod.getSimpleName().toLowerCase().contains(ctField.getSimpleName().toLowerCase())) {
                    if (!ctMethod.getType().equals(ctField.getType())) {
                        continue;
                    }
                    if (ctMethod.getParameters().size() != 0) {
                        continue;
                    }
                    return ctMethod;
                }
            }
            // Search with template.
            final CtBlock<?> templateRoot = GETTER_TEMPLATE_MATCHER_CLASS.getMethod("getElement").getBody();
            ((CtReturn) templateRoot.getStatement(0)).setReturnedExpression(factory.Code().createVariableRead(ctField.getReference(), true));
            List<CtMethod> matchers = ctField.getDeclaringType().getElements(new TypeFilter<CtMethod>(CtMethod.class) {

                @Override
                public boolean matches(CtMethod element) {
                    return element.getBody().toString().equals(templateRoot.toString());
                }
            });
            if (matchers.isEmpty()) {
                throw new SpoonException("No getter found for field " + ctField);
            }
            if (matchers.size() > 1) {
                throw new SpoonException("Get more than one getter (" + StringUtils.join(matchers, ";") + "). Please make an more ingenious method to get getter method.");
            }
            return matchers.get(0);
        }

        /**
         * Check if the type is a subtype of CtElement.
         */
        private boolean isSubTypeOfCtElement(CtTypeReference<?> type) {
            if (!type.isPrimitive() && !type.equals(factory.Type().STRING)) {
                if (type.isSubtypeOf(factory.Type().createReference(CtElement.class))) {
                    return true;
                }
                if (type.getQualifiedName().equals(LIST_REFERENCE.getQualifiedName()) || type.getQualifiedName().equals(COLLECTION_REFERENCE.getQualifiedName()) || type.getQualifiedName().equals(SET_REFERENCE.getQualifiedName())) {
                    if (type.getActualTypeArguments().get(0).isSubtypeOf(CTELEMENT_REFERENCE)) {
                        return true;
                    }
                }
            }
            return false;
        }

        private boolean isConstantOrStatic(CtField<?> ctField) {
            return ctField.getModifiers().contains(ModifierKind.FINAL) || ctField.getModifiers().contains(ModifierKind.STATIC);
        }
    }.scan(getFactory().Class().get(CtInheritanceScanner.class));
}
Also used : CtParameter(spoon.reflect.declaration.CtParameter) CtInvocation(spoon.reflect.code.CtInvocation) CtTypeReference(spoon.reflect.reference.CtTypeReference) CtExecutableReference(spoon.reflect.reference.CtExecutableReference) ArrayList(java.util.ArrayList) List(java.util.List) CtInterface(spoon.reflect.declaration.CtInterface) CtAssignment(spoon.reflect.code.CtAssignment) CtElement(spoon.reflect.declaration.CtElement) CtLocalVariable(spoon.reflect.code.CtLocalVariable) CtIf(spoon.reflect.code.CtIf) CtType(spoon.reflect.declaration.CtType) CtConstructorCall(spoon.reflect.code.CtConstructorCall) Collection(java.util.Collection) CtScanner(spoon.reflect.visitor.CtScanner) CtMethod(spoon.reflect.declaration.CtMethod) CtComment(spoon.reflect.code.CtComment) CtFieldAccess(spoon.reflect.code.CtFieldAccess) Set(java.util.Set) Factory(spoon.reflect.factory.Factory) TypeFilter(spoon.reflect.visitor.filter.TypeFilter) CtThrow(spoon.reflect.code.CtThrow) CtField(spoon.reflect.declaration.CtField) CtInheritanceScanner(spoon.reflect.visitor.CtInheritanceScanner) OverridingMethodFilter(spoon.reflect.visitor.filter.OverridingMethodFilter) CtVariableAccess(spoon.reflect.code.CtVariableAccess) SpoonException(spoon.SpoonException) CtExpression(spoon.reflect.code.CtExpression) CtBlock(spoon.reflect.code.CtBlock)

Example 10 with CtInterface

use of spoon.reflect.declaration.CtInterface in project spoon by INRIA.

the class LambdaTest method testLambdaFilter.

@Test
public void testLambdaFilter() throws Exception {
    // check constructor with CtInterface
    List<String> methodNames = foo.filterChildren(new LambdaFilter((CtInterface<?>) foo.getNestedType("CheckPerson"))).map((CtLambda l) -> l.getParent(CtMethod.class).getSimpleName()).list();
    assertHasStrings(methodNames);
    // check constructor with CtTypeReference
    methodNames = foo.filterChildren(new LambdaFilter(foo.getNestedType("Check").getReference())).map((CtLambda l) -> l.getParent(CtMethod.class).getSimpleName()).list();
    assertHasStrings(methodNames, "m", "m6");
    // check empty constructor and addImplementingInterface with Interface
    methodNames = foo.filterChildren(new LambdaFilter().addImplementingInterface((CtInterface<?>) foo.getNestedType("CheckPersons"))).map((CtLambda l) -> l.getParent(CtMethod.class).getSimpleName()).list();
    assertHasStrings(methodNames, "m3", "m5");
    // check empty constructor and addImplementingInterface with CtTypeReference
    methodNames = foo.filterChildren(new LambdaFilter().addImplementingInterface(factory.createCtTypeReference(Predicate.class))).map((CtLambda l) -> l.getParent(CtMethod.class).getSimpleName()).list();
    assertHasStrings(methodNames, "m2", "m4", "m7", "m8");
}
Also used : CtInterface(spoon.reflect.declaration.CtInterface) LambdaFilter(spoon.reflect.visitor.filter.LambdaFilter) CtLambda(spoon.reflect.code.CtLambda) CtMethod(spoon.reflect.declaration.CtMethod) Predicate(java.util.function.Predicate) Test(org.junit.Test)

Aggregations

CtInterface (spoon.reflect.declaration.CtInterface)12 CtMethod (spoon.reflect.declaration.CtMethod)8 Test (org.junit.Test)7 CtClass (spoon.reflect.declaration.CtClass)7 Launcher (spoon.Launcher)6 CtType (spoon.reflect.declaration.CtType)4 ArrayList (java.util.ArrayList)3 SpoonException (spoon.SpoonException)3 CtElement (spoon.reflect.declaration.CtElement)3 Factory (spoon.reflect.factory.Factory)3 CtBlock (spoon.reflect.code.CtBlock)2 CtIf (spoon.reflect.code.CtIf)2 CtConstructor (spoon.reflect.declaration.CtConstructor)2 CtField (spoon.reflect.declaration.CtField)2 CtParameter (spoon.reflect.declaration.CtParameter)2 CtTypeParameter (spoon.reflect.declaration.CtTypeParameter)2 CtInheritanceScanner (spoon.reflect.visitor.CtInheritanceScanner)2 File (java.io.File)1 Collection (java.util.Collection)1 List (java.util.List)1