Search in sources :

Example 11 with CtScanner

use of spoon.reflect.visitor.CtScanner in project spoon by INRIA.

the class ReplaceParametrizedTest method testContract.

@Test
public void testContract() throws Throwable {
    List<String> problems = new ArrayList<>();
    // contract: all elements are replaceable wherever they are in the model
    // this test puts them at all possible locations
    CtType<?> toTest = typeToTest.getModelInterface();
    CtElement o = factory.Core().create((Class<? extends CtElement>) toTest.getActualClass());
    for (MetamodelProperty mmField : typeToTest.getRoleToProperty().values()) {
        Class<?> argType = mmField.getItemValueType().getActualClass();
        if (!CtElement.class.isAssignableFrom(argType)) {
            continue;
        }
        CtTypeReference<?> itemType = mmField.getItemValueType();
        // special cases...
        if (itemType.getQualifiedName().equals(CtStatement.class.getName())) {
            // the children of CtLoop wraps CtStatement into an implicit CtBlock. So make a block directly to test plain get/set and not wrapping.
            itemType = factory.createCtTypeReference(CtBlock.class);
        }
        if (o.getClass().getSimpleName().equals("CtAnnotationFieldAccessImpl") && mmField.getRole() == CtRole.VARIABLE) {
            itemType = factory.createCtTypeReference(CtFieldReference.class);
        } else if (CtFieldAccess.class.isAssignableFrom(o.getClass()) && mmField.getRole() == CtRole.VARIABLE) {
            itemType = factory.createCtTypeReference(CtFieldReference.class);
        }
        CtElement argument = (CtElement) createCompatibleObject(itemType);
        assertNotNull(argument);
        // we create a fresh object
        CtElement receiver = ((CtElement) o).clone();
        RoleHandler rh = RoleHandlerHelper.getRoleHandler(o.getClass(), mmField.getRole());
        if (mmField.isUnsettable()) {
            try {
                // we invoke the setter
                invokeSetter(rh, receiver, argument);
            } catch (SpoonException e) {
                // ok this unsettable property has no setter at all
                return;
            }
            // this unsettable property has setter, but it should do nothing
            CtRole argumentsRoleInParent = argument.getRoleInParent();
            if (argumentsRoleInParent == null) {
                // OK - unsettable property set no value
                continue;
            }
            if (argumentsRoleInParent == mmField.getRole()) {
                problems.add("UnsettableProperty " + mmField + " sets the value");
            } else {
                if (mmField.isDerived()) {
                // it is OK, that setting of value into derived unsettable field influences other field
                // Example 1: CtCatchVariable.setType(x) influences result of getMultitype()
                // Example 2: CtEnumValue.setAssignment(x) influences result of getDefaultExpression()
                } else {
                    problems.add("UnsettableProperty " + mmField + " sets the value into different role " + argumentsRoleInParent);
                }
            }
            continue;
        }
        // we invoke the setter
        invokeSetter(rh, receiver, argument);
        // contract: a property setter sets properties that are visitable by a scanner
        CtElement finalArgument = argument;
        class Scanner extends CtScanner {

            boolean found = false;

            @Override
            public void scan(CtRole role, CtElement e) {
                super.scan(role, e);
                if (e == finalArgument) {
                    if (rh.getRole() == role || rh.getRole().getSuperRole() == role) {
                        found = true;
                        return;
                    }
                    // if (rh.getRole()==CtRole.TYPE && role==CtRole.MULTI_TYPE) {
                    // //CtCatchVaraible#type sets CtCatchVaraible#multiType - OK
                    // found = true;
                    // return;
                    // }
                    problems.add("Argument was set into " + rh.getRole() + " but was found in " + role);
                }
            }
        }
        ;
        Scanner s = new Scanner();
        receiver.accept(s);
        assertTrue("Settable field " + mmField.toString() + " should set value.\n" + getReport(problems), s.found);
        // contract: a property getter on the same role can be used to get the value back
        assertSame(argument, invokeGetter(rh, receiver));
        final CtElement argument2 = argument.clone();
        assertNotSame(argument, argument2);
        // we do the replace
        argument.replace(argument2);
        // the new element is indeed now in this AST
        assertTrue(receiver.getClass().getSimpleName() + " failed for " + mmField, receiver.getElements(new Filter<CtElement>() {

            @Override
            public boolean matches(CtElement element) {
                return element == argument2;
            }
        }).size() == 1);
    }
    if (problems.size() > 0) {
        fail(getReport(problems));
    }
}
Also used : CtScanner(spoon.reflect.visitor.CtScanner) SpoonException(spoon.SpoonException) CtElement(spoon.reflect.declaration.CtElement) RoleHandler(spoon.reflect.meta.RoleHandler) ArrayList(java.util.ArrayList) CtFieldReference(spoon.reflect.reference.CtFieldReference) CtBlock(spoon.reflect.code.CtBlock) CtStatement(spoon.reflect.code.CtStatement) Filter(spoon.reflect.visitor.Filter) MetamodelProperty(spoon.test.metamodel.MetamodelProperty) CtRole(spoon.reflect.path.CtRole) CtScanner(spoon.reflect.visitor.CtScanner) Test(org.junit.Test)

Example 12 with CtScanner

use of spoon.reflect.visitor.CtScanner in project spoon by INRIA.

the class CloneTest method testCloneMethodsDeclaredInAST.

@Test
public void testCloneMethodsDeclaredInAST() throws Exception {
    final Launcher launcher = new Launcher();
    launcher.setArgs(new String[] { "--output-type", "nooutput" });
    launcher.getEnvironment().setNoClasspath(true);
    // interfaces.
    launcher.addInputResource("./src/main/java/spoon/reflect/code");
    launcher.addInputResource("./src/main/java/spoon/reflect/declaration");
    launcher.addInputResource("./src/main/java/spoon/reflect/reference");
    // implementations.
    launcher.addInputResource("./src/main/java/spoon/support/reflect/code");
    launcher.addInputResource("./src/main/java/spoon/support/reflect/declaration");
    launcher.addInputResource("./src/main/java/spoon/support/reflect/reference");
    launcher.run();
    new CtScanner() {

        @Override
        public <T> void visitCtClass(CtClass<T> ctClass) {
            if (!ctClass.getSimpleName().startsWith("Ct")) {
                return;
            }
            final CtMethod<Object> clone = ctClass.getMethod("clone");
            assertNotNull(ctClass.getQualifiedName() + " hasn't clone method.", clone);
            assertTrue(ctClass.getQualifiedName() + " hasn't Override annotation on clone method.", clone.getAnnotations().stream().map(ctAnnotation -> ctAnnotation.getActualAnnotation().annotationType()).collect(Collectors.toList()).contains(Override.class));
        }

        @Override
        public <T> void visitCtInterface(CtInterface<T> intrface) {
            if (!intrface.getSimpleName().startsWith("Ct")) {
                return;
            }
            final CtMethod<Object> clone = intrface.getMethod("clone");
            if (hasConcreteImpl(intrface)) {
                assertNotNull(intrface.getQualifiedName() + " hasn't clone method.", clone);
                if (!isRootDeclaration(intrface)) {
                    assertTrue(intrface.getQualifiedName() + " hasn't Override annotation on clone method.", clone.getAnnotations().stream().map(ctAnnotation -> ctAnnotation.getActualAnnotation().annotationType()).collect(Collectors.toList()).contains(Override.class));
                }
            }
        }

        private <T> boolean hasConcreteImpl(CtInterface<T> intrface) {
            return Query.getElements(intrface.getFactory(), new TypeFilter<CtClass<?>>(CtClass.class) {

                @Override
                public boolean matches(CtClass<?> element) {
                    return super.matches(element) && element.getSuperInterfaces().contains(intrface.getReference());
                }
            }).size() > 0;
        }

        private <T> boolean isRootDeclaration(CtInterface<T> intrface) {
            return "CtElement".equals(intrface.getSimpleName());
        }
    }.scan(launcher.getModel().getRootPackage());
}
Also used : CtClass(spoon.reflect.declaration.CtClass) Launcher(spoon.Launcher) TypeFilter(spoon.reflect.visitor.filter.TypeFilter) CtScanner(spoon.reflect.visitor.CtScanner) CtMethod(spoon.reflect.declaration.CtMethod) Test(org.junit.Test)

Example 13 with CtScanner

use of spoon.reflect.visitor.CtScanner in project spoon by INRIA.

the class CompilationTest method testExoticClassLoader.

@Test
public void testExoticClassLoader() throws Exception {
    // contract: Spoon uses the exotic class loader
    final List<String> l = new ArrayList<>();
    class MyClassLoader extends ClassLoader {

        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            l.add(name);
            return super.loadClass(name, resolve);
        }
    }
    Launcher launcher = new Launcher();
    launcher.getEnvironment().setInputClassLoader(new MyClassLoader());
    launcher.getEnvironment().setNoClasspath(true);
    launcher.addInputResource("src/test/resources/reference-test/Foo.java");
    launcher.buildModel();
    launcher.getModel().getRootPackage().accept(new CtScanner() {

        @Override
        public <T> void visitCtTypeReference(CtTypeReference<T> reference) {
            try {
                // forcing loading it
                reference.getTypeDeclaration();
            } catch (SpoonClassNotFoundException ignore) {
            }
        }
    });
    assertEquals(3, l.size());
    assertTrue(l.contains("KJHKY"));
    assertEquals(MyClassLoader.class, launcher.getEnvironment().getInputClassLoader().getClass());
}
Also used : ArrayList(java.util.ArrayList) URLClassLoader(java.net.URLClassLoader) Launcher(spoon.Launcher) SpoonClassNotFoundException(spoon.support.SpoonClassNotFoundException) CtScanner(spoon.reflect.visitor.CtScanner) Test(org.junit.Test)

Example 14 with CtScanner

use of spoon.reflect.visitor.CtScanner in project spoon by INRIA.

the class JDTCommentBuilder method findCommentParent.

/**
 * Find the parent of a comment based on the position
 * @param comment the comment
 * @return the parent of the comment
 */
private CtElement findCommentParent(CtComment comment) {
    class FindCommentParentScanner extends CtScanner {

        public CtElement commentParent;

        private int start;

        private int end;

        FindCommentParentScanner(int start, int end) {
            this.start = start;
            this.end = end;
        }

        private boolean isCommentBetweenElementPosition(CtElement element) {
            return (element.getPosition() != null && element.getPosition().getSourceStart() <= this.start && element.getPosition().getSourceEnd() >= this.end);
        }

        @Override
        public void scan(CtElement element) {
            if (element == null) {
                return;
            }
            if (element.isImplicit()) {
                return;
            }
            CtElement body = getBody(element);
            if (body != null && body.getPosition() == null) {
                body = null;
            }
            boolean betweenElementPosition = this.isCommentBetweenElementPosition(element);
            boolean bodyBetweenElementPosition = (body != null) && this.isCommentBetweenElementPosition(body);
            if (betweenElementPosition || bodyBetweenElementPosition) {
                commentParent = element;
                element.accept(this);
            }
        }
    }
    FindCommentParentScanner findCommentParentScanner = new FindCommentParentScanner(comment.getPosition().getSourceStart(), comment.getPosition().getSourceEnd());
    if (!spoonUnit.getDeclaredTypes().isEmpty()) {
        findCommentParentScanner.scan(spoonUnit.getDeclaredTypes());
    } else if (spoonUnit.getDeclaredModule() != null) {
        findCommentParentScanner.scan(spoonUnit.getDeclaredModule());
    }
    return findCommentParentScanner.commentParent;
}
Also used : CtElement(spoon.reflect.declaration.CtElement) CtScanner(spoon.reflect.visitor.CtScanner)

Example 15 with CtScanner

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

Aggregations

CtScanner (spoon.reflect.visitor.CtScanner)18 CtElement (spoon.reflect.declaration.CtElement)9 Test (org.junit.Test)7 Launcher (spoon.Launcher)6 ArrayList (java.util.ArrayList)4 SpoonException (spoon.SpoonException)4 CtType (spoon.reflect.declaration.CtType)3 CtBlock (spoon.reflect.code.CtBlock)2 CtComment (spoon.reflect.code.CtComment)2 CtLocalVariable (spoon.reflect.code.CtLocalVariable)2 CtClass (spoon.reflect.declaration.CtClass)2 CtExecutable (spoon.reflect.declaration.CtExecutable)2 CtField (spoon.reflect.declaration.CtField)2 CtMethod (spoon.reflect.declaration.CtMethod)2 CtParameter (spoon.reflect.declaration.CtParameter)2 CtLocalVariableReference (spoon.reflect.reference.CtLocalVariableReference)2 CtTypeReference (spoon.reflect.reference.CtTypeReference)2 TypeFilter (spoon.reflect.visitor.filter.TypeFilter)2 URLClassLoader (java.net.URLClassLoader)1 ArrayDeque (java.util.ArrayDeque)1