Search in sources :

Example 16 with CtComment

use of spoon.reflect.code.CtComment in project spoon by INRIA.

the class CommentTest method testInLineComment.

@Test
public void testInLineComment() {
    Factory f = getSpoonFactory();
    CtClass<?> type = (CtClass<?>) f.Type().get(InlineComment.class);
    String strType = type.toString();
    List<CtComment> comments = type.getElements(new TypeFilter<CtComment>(CtComment.class));
    // verify that the number of comment present in the AST is correct
    assertEquals(64, comments.size());
    // verify that all comments present in the AST is printed
    for (CtComment comment : comments) {
        if (comment.getCommentType() == CtComment.CommentType.FILE) {
            // the header of the file is not printed with the toString
            continue;
        }
        assertNotNull(comment.getParent());
        assertTrue(comment.toString() + ":" + comment.getParent() + " is not printed", strType.contains(comment.toString()));
    }
    assertEquals(3, type.getComments().size());
    assertEquals(CtComment.CommentType.FILE, type.getComments().get(0).getCommentType());
    assertEquals(createFakeComment(f, "comment class"), type.getComments().get(1));
    CtField<?> field = type.getField("field");
    assertEquals(3, field.getComments().size());
    assertEquals(createFakeComment(f, "Comment Field"), field.getComments().get(0));
    assertEquals("// Comment Field" + newLine + "// comment field 2" + newLine + "// comment in field" + newLine + "private int field = 10;", field.toString());
    CtAnonymousExecutable ctAnonymousExecutable = type.getAnonymousExecutables().get(0);
    assertEquals(1, ctAnonymousExecutable.getComments().size());
    assertEquals(createFakeComment(f, "comment static block"), ctAnonymousExecutable.getComments().get(0));
    assertEquals(createFakeComment(f, "comment inside static"), ctAnonymousExecutable.getBody().getStatement(0));
    assertEquals("// comment static block" + newLine + "static {" + newLine + "    // comment inside static" + newLine + "}", ctAnonymousExecutable.toString());
    CtConstructor constructor = type.getConstructor();
    assertEquals(1, constructor.getComments().size());
    assertEquals(createFakeComment(f, "comment constructor"), constructor.getComments().get(0));
    // index 0 is the implicit super call
    assertEquals(createFakeComment(f, "Comment in constructor"), constructor.getBody().getStatement(1));
    assertEquals("// comment constructor" + newLine + "public InlineComment() {" + newLine + "    // Comment in constructor" + newLine + "}", constructor.toString());
    CtMethod<Object> m = type.getMethod("m");
    assertEquals(1, m.getComments().size());
    assertEquals(createFakeComment(f, "comment method"), m.getComments().get(0));
    assertEquals(createFakeComment(f, "comment empty method block"), m.getBody().getStatement(0));
    assertEquals("// comment method" + newLine + "public void m() {" + newLine + "    // comment empty method block" + newLine + "}", m.toString());
    CtMethod<Object> m1 = type.getMethod("m1");
    CtSwitch ctSwitch = m1.getBody().getStatement(0);
    assertEquals(createFakeComment(f, "comment switch"), ctSwitch.getComments().get(0));
    assertEquals("// comment switch" + newLine + "switch (1) {" + newLine + "    // before first case" + newLine + "    case 0 :" + newLine + "        // comment case 0: empty case" + newLine + "    case 1 :" + newLine + "        // comment case 1" + newLine + "        int i = 0;" + newLine + "    default :" + newLine + "        // comment default" + newLine + "}", ctSwitch.toString());
    CtFor ctFor = m1.getBody().getStatement(1);
    assertEquals(createFakeComment(f, "comment for"), ctFor.getComments().get(0));
    assertEquals("// comment for" + newLine + "for (int i = 0; i < 10; i++) {" + newLine + "    // comment for block" + newLine + "}", ctFor.toString());
    CtIf ctIf = m1.getBody().getStatement(2);
    assertEquals(createFakeComment(f, "comment if"), ctIf.getComments().get(0));
    assertEquals("// comment if" + newLine + "if ((1 % 2) == 0) {" + newLine + "    // comment unary operator" + newLine + "    (field)++;" + newLine + "}", ctIf.toString());
    CtConstructorCall ctConstructorCall = m1.getBody().getStatement(3);
    assertEquals(createFakeComment(f, "comment constructor call"), ctConstructorCall.getComments().get(0));
    assertEquals("// comment constructor call" + newLine + "new spoon.test.comment.testclasses.InlineComment()", ctConstructorCall.toString());
    CtInvocation ctInvocation = m1.getBody().getStatement(4);
    assertEquals(createFakeComment(f, "comment invocation"), ctInvocation.getComments().get(0));
    assertEquals("// comment invocation" + newLine + "this.m()", ctInvocation.toString());
    CtLocalVariable ctLocalVariable = m1.getBody().getStatement(5);
    assertEquals(createFakeComment(f, "comment local variable"), ctLocalVariable.getComments().get(0));
    assertEquals("// comment local variable" + newLine + "int i = 0", ctLocalVariable.toString());
    CtLocalVariable ctLocalVariable2 = m1.getBody().getStatement(6);
    assertEquals(createFakeComment(f, "comment multi assignments"), ctLocalVariable2.getComments().get(0));
    assertEquals("// comment multi assignments" + newLine + "int j = 2", ctLocalVariable2.toString());
    CtDo ctDo = m1.getBody().getStatement(7);
    assertEquals(createFakeComment(f, "comment dowhile"), ctDo.getComments().get(0));
    assertEquals("// comment dowhile" + newLine + "do {" + newLine + "    // comment in do while" + newLine + "    i++;" + newLine + "    // comment end do while" + newLine + "} while (i < 10 )", ctDo.toString());
    CtTry ctTry = m1.getBody().getStatement(8);
    assertEquals(createFakeComment(f, "comment try"), ctTry.getComments().get(0));
    assertEquals("// comment try" + newLine + "try {" + newLine + "    // comment in try" + newLine + "    i++;" + newLine + "}// between" + newLine + "// try/catch" + newLine + " catch (java.lang.Exception e) {" + newLine + "    // comment in catch" + newLine + "}", ctTry.toString());
    CtSynchronized ctSynchronized = m1.getBody().getStatement(9);
    assertEquals(createFakeComment(f, "comment synchronized"), ctSynchronized.getComments().get(0));
    assertEquals("// comment synchronized" + newLine + "synchronized(this) {" + newLine + "    // comment in synchronized" + newLine + "}", ctSynchronized.toString());
    CtLocalVariable ctLocalVariable1 = m1.getBody().getStatement(10);
    CtConditional ctConditional = (CtConditional) ctLocalVariable1.getDefaultExpression();
    assertEquals(createFakeComment(f, "comment after condition CtConditional"), ctConditional.getCondition().getComments().get(0));
    assertEquals(createFakeComment(f, "comment before then CtConditional"), ctConditional.getThenExpression().getComments().get(0));
    assertEquals(createFakeComment(f, "comment after then CtConditional"), ctConditional.getThenExpression().getComments().get(1));
    assertEquals(createFakeComment(f, "comment before else CtConditional"), ctConditional.getElseExpression().getComments().get(0));
    assertEquals(createFakeComment(f, "comment after else CtConditional"), ctLocalVariable1.getComments().get(0));
    assertEquals("java.lang.Double dou = (i == 1// comment after condition CtConditional" + newLine + ") ? // comment before then CtConditional" + newLine + "null// comment after then CtConditional" + newLine + " : // comment before else CtConditional" + newLine + "new java.lang.Double((j / ((double) (i - 1))))", ctLocalVariable1.toString());
    CtNewArray ctNewArray = (CtNewArray) ((CtLocalVariable) m1.getBody().getStatement(11)).getDefaultExpression();
    assertEquals(createFakeComment(f, "last comment at the end of array"), ctNewArray.getComments().get(0));
    CtElement arrayValue = (CtElement) ctNewArray.getElements().get(0);
    assertEquals(createFakeComment(f, "comment before array value"), arrayValue.getComments().get(0));
    assertEquals(createFakeComment(f, "comment after array value"), arrayValue.getComments().get(1));
    CtLocalVariable ctLocalVariableString = m1.getBody().getStatement(12);
    assertEquals(createFakeComment(f, "comment multi line string"), ((CtBinaryOperator) ((CtBinaryOperator) ctLocalVariableString.getDefaultExpression()).getRightHandOperand()).getLeftHandOperand().getComments().get(0));
    assertEquals("\"\" + (\"\"// comment multi line string" + newLine + " + \"\")", ctLocalVariableString.getDefaultExpression().toString());
    ctLocalVariable1 = m1.getBody().getStatement(13);
    ctConditional = (CtConditional) ctLocalVariable1.getDefaultExpression();
    assertEquals("boolean c = (i == 1) ? // comment before then boolean CtConditional" + newLine + "i == 1// comment after then boolean CtConditional" + newLine + " : i == 2", ctLocalVariable1.toString());
    CtReturn ctReturn = m1.getBody().getStatement(14);
    assertEquals(createFakeComment(f, "comment return"), ctReturn.getComments().get(0));
    assertEquals("// comment return" + newLine + "return", ctReturn.toString());
    CtMethod m2 = type.getMethodsByName("m2").get(0);
    assertEquals(6, m2.getComments().size());
    CtParameter ctParameter = (CtParameter) m2.getParameters().get(0);
    assertEquals(4, ctParameter.getComments().size());
    assertEquals("// comment before type" + newLine + "// comment after parameter" + newLine + "// comment before throws" + newLine + "// comment before exception 1" + newLine + "// comment before exception 2" + newLine + "// comment before block" + newLine + "public void m2(// comment before name" + newLine + "// comment before parameters" + newLine + "// comment before type parameter" + newLine + "// comment before name parameter" + newLine + "int i) throws java.lang.Error, java.lang.Exception {" + newLine + "}", m2.toString());
}
Also used : CtConditional(spoon.reflect.code.CtConditional) CtComment(spoon.reflect.code.CtComment) CtSwitch(spoon.reflect.code.CtSwitch) DefaultCoreFactory(spoon.support.DefaultCoreFactory) Factory(spoon.reflect.factory.Factory) CtParameter(spoon.reflect.declaration.CtParameter) CtTry(spoon.reflect.code.CtTry) CtNewArray(spoon.reflect.code.CtNewArray) CtSynchronized(spoon.reflect.code.CtSynchronized) InlineComment(spoon.test.comment.testclasses.InlineComment) CtInvocation(spoon.reflect.code.CtInvocation) CtReturn(spoon.reflect.code.CtReturn) CtDo(spoon.reflect.code.CtDo) CtFor(spoon.reflect.code.CtFor) CtBinaryOperator(spoon.reflect.code.CtBinaryOperator) CtElement(spoon.reflect.declaration.CtElement) CtLocalVariable(spoon.reflect.code.CtLocalVariable) CtIf(spoon.reflect.code.CtIf) CtConstructor(spoon.reflect.declaration.CtConstructor) CtClass(spoon.reflect.declaration.CtClass) CtConstructorCall(spoon.reflect.code.CtConstructorCall) CtAnonymousExecutable(spoon.reflect.declaration.CtAnonymousExecutable) CtMethod(spoon.reflect.declaration.CtMethod) Test(org.junit.Test)

Example 17 with CtComment

use of spoon.reflect.code.CtComment in project spoon by INRIA.

the class CommentTest method testCodeFactory.

@Test
public void testCodeFactory() {
    Factory spoonFactory = getSpoonFactory();
    CtComment comment = spoonFactory.Code().createComment("comment", CtComment.CommentType.INLINE);
    assertEquals("// comment", comment.toString());
    assertEquals(CtComment.CommentType.INLINE, comment.getCommentType());
    comment = spoonFactory.Code().createInlineComment("comment");
    assertEquals("// comment", comment.toString());
    assertEquals(CtComment.CommentType.INLINE, comment.getCommentType());
}
Also used : CtComment(spoon.reflect.code.CtComment) DefaultCoreFactory(spoon.support.DefaultCoreFactory) Factory(spoon.reflect.factory.Factory) Test(org.junit.Test)

Example 18 with CtComment

use of spoon.reflect.code.CtComment in project spoon by INRIA.

the class ReplaceScanner method visitCtMethod.

@Override
public <T> void visitCtMethod(CtMethod<T> element) {
    if (!element.getSimpleName().startsWith("visitCt")) {
        return;
    }
    Factory factory = element.getFactory();
    CtMethod<T> clone = element.clone();
    factory.Annotation().annotate(clone, Override.class);
    clone.getBody().getStatements().clear();
    for (int i = 1; i < element.getBody().getStatements().size() - 1; i++) {
        CtInvocation inv = element.getBody().getStatement(i);
        List<CtExpression<?>> invArgs = new ArrayList<>(inv.getArguments());
        if (invArgs.size() <= 1) {
            throw new RuntimeException("You forget the role argument in line " + i + " of method " + element.getSimpleName() + " from " + element.getDeclaringType().getQualifiedName());
        }
        // remove role argument
        invArgs.remove(0);
        CtInvocation getter = (CtInvocation) invArgs.get(0);
        if (clone.getComments().size() == 0) {
            // Add auto-generated comment.
            final CtComment comment = factory.Core().createComment();
            comment.setCommentType(CtComment.CommentType.INLINE);
            comment.setContent("auto-generated, see " + ReplacementVisitorGenerator.class.getName());
            clone.addComment(comment);
        }
        Class actualClass = getter.getType().getActualClass();
        CtInvocation<?> invocation = createInvocation(factory, element, invArgs, getter, actualClass);
        clone.getBody().addStatement(invocation);
    }
    target.addMethod(clone);
}
Also used : CtComment(spoon.reflect.code.CtComment) CtInvocation(spoon.reflect.code.CtInvocation) CtExpression(spoon.reflect.code.CtExpression) ArrayList(java.util.ArrayList) Factory(spoon.reflect.factory.Factory) CtClass(spoon.reflect.declaration.CtClass)

Example 19 with CtComment

use of spoon.reflect.code.CtComment in project spoon by INRIA.

the class JDTCommentBuilder method addCommentToNear.

/**
 * Insert the element to nearer element in the elements collections
 * @param comment the comment to insert
 * @param elements the collection that content the ast elements
 * @return
 */
private CtElement addCommentToNear(final CtComment comment, final Collection<CtElement> elements) {
    CtElement best = null;
    int smallDistance = Integer.MAX_VALUE;
    for (CtElement element : elements) {
        if (element.getPosition() == null) {
            continue;
        }
        if (element.isImplicit()) {
            continue;
        }
        if (element instanceof CtComment) {
            continue;
        }
        final boolean isAfter = element.getPosition().getSourceEnd() < comment.getPosition().getSourceStart();
        int distance = Math.abs(element.getPosition().getSourceStart() - comment.getPosition().getSourceEnd());
        if (isAfter) {
            distance = Math.abs(element.getPosition().getSourceEnd() - comment.getPosition().getSourceStart());
        }
        int elementEndLine = element.getPosition().getEndLine();
        int commentLine = comment.getPosition().getLine();
        if (distance < smallDistance && (!isAfter || elementEndLine == commentLine)) {
            best = element;
            smallDistance = distance;
        }
    }
    // adds the comment to the nearest element
    if (best != null) {
        best.addComment(comment);
    }
    return best;
}
Also used : CtComment(spoon.reflect.code.CtComment) CtElement(spoon.reflect.declaration.CtElement)

Example 20 with CtComment

use of spoon.reflect.code.CtComment 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

CtComment (spoon.reflect.code.CtComment)24 Test (org.junit.Test)12 Factory (spoon.reflect.factory.Factory)12 DefaultCoreFactory (spoon.support.DefaultCoreFactory)8 CtClass (spoon.reflect.declaration.CtClass)7 ArrayList (java.util.ArrayList)5 Launcher (spoon.Launcher)4 CtInvocation (spoon.reflect.code.CtInvocation)4 CtLocalVariable (spoon.reflect.code.CtLocalVariable)4 CtElement (spoon.reflect.declaration.CtElement)4 CtMethod (spoon.reflect.declaration.CtMethod)4 List (java.util.List)3 CtConstructorCall (spoon.reflect.code.CtConstructorCall)3 CtExpression (spoon.reflect.code.CtExpression)3 CtIf (spoon.reflect.code.CtIf)3 CtParameter (spoon.reflect.declaration.CtParameter)3 Collection (java.util.Collection)2 HashMap (java.util.HashMap)2 CtAssignment (spoon.reflect.code.CtAssignment)2 CtBlock (spoon.reflect.code.CtBlock)2