From 9987eff2acd7d86cbdb8dfd740cdb4f529e23c62 Mon Sep 17 00:00:00 2001 From: tdurieux Date: Wed, 9 Mar 2016 10:17:25 +0100 Subject: [PATCH] feat(comment): WIP Adds comments in the AST --- src/main/java/spoon/compiler/Environment.java | 10 + .../java/spoon/reflect/code/CtComment.java | 33 ++ .../spoon/reflect/declaration/CtElement.java | 13 + .../spoon/reflect/factory/CoreFactory.java | 6 + .../reflect/visitor/CtAbstractVisitor.java | 10 +- .../reflect/visitor/CtInheritanceScanner.java | 9 + .../java/spoon/reflect/visitor/CtScanner.java | 62 +++ .../java/spoon/reflect/visitor/CtVisitor.java | 6 + .../visitor/DefaultJavaPrettyPrinter.java | 68 +++- .../spoon/support/DefaultCoreFactory.java | 8 + .../spoon/support/StandardEnvironment.java | 10 + .../compiler/jdt/JDTBasedSpoonCompiler.java | 9 +- .../compiler/jdt/JDTBatchCompiler.java | 26 ++ .../compiler/jdt/JDTCommentBuilder.java | 353 ++++++++++++++++++ .../support/compiler/jdt/JDTTreeBuilder.java | 3 +- .../support/reflect/code/CtCommentImpl.java | 55 +++ .../CtAnonymousExecutableImpl.java | 2 +- .../reflect/declaration/CtElementImpl.java | 58 ++- .../reflect/eval/VisitorPartialEvaluator.java | 6 + .../java/spoon/template/TemplateMatcher.java | 28 +- .../java/spoon/test/comment/CommentTest.java | 175 +++++++++ .../comment/testclasses/InlineComment.java | 82 ++++ .../spoon/test/prettyprinter/LinesTest.java | 6 +- 23 files changed, 1002 insertions(+), 36 deletions(-) create mode 100644 src/main/java/spoon/reflect/code/CtComment.java create mode 100644 src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java create mode 100644 src/main/java/spoon/support/reflect/code/CtCommentImpl.java create mode 100644 src/test/java/spoon/test/comment/CommentTest.java create mode 100644 src/test/java/spoon/test/comment/testclasses/InlineComment.java diff --git a/src/main/java/spoon/compiler/Environment.java b/src/main/java/spoon/compiler/Environment.java index e6c6f50b073..5d55d667518 100644 --- a/src/main/java/spoon/compiler/Environment.java +++ b/src/main/java/spoon/compiler/Environment.java @@ -296,6 +296,16 @@ void report(Processor processor, Level level, */ void setGenerateJavadoc(boolean generateJavadoc); + /** + * Returns the value of the option generate-comments. + */ + boolean isGenerateComments(); + + /** + * Sets the option generate-comments to generate comments of the project on the source generated. + */ + void setGenerateComments(boolean generateComments); + /** * Gets the factory of the environment. */ diff --git a/src/main/java/spoon/reflect/code/CtComment.java b/src/main/java/spoon/reflect/code/CtComment.java new file mode 100644 index 00000000000..2eba8f3d6f9 --- /dev/null +++ b/src/main/java/spoon/reflect/code/CtComment.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2006-2015 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.reflect.code; + +import spoon.reflect.declaration.CtElement; + +/** + * This code element defines a comment + * + */ +public interface CtComment extends CtElement, CtStatement { + String getContent(); + + boolean isInline(); + + E setContent(String content); + + E setInline(boolean inline); +} diff --git a/src/main/java/spoon/reflect/declaration/CtElement.java b/src/main/java/spoon/reflect/declaration/CtElement.java index 56332d30e93..d7a04cd54b8 100644 --- a/src/main/java/spoon/reflect/declaration/CtElement.java +++ b/src/main/java/spoon/reflect/declaration/CtElement.java @@ -17,6 +17,7 @@ package spoon.reflect.declaration; import spoon.processing.FactoryAccessor; +import spoon.reflect.code.CtComment; import spoon.reflect.cu.SourcePosition; import spoon.reflect.reference.CtReference; import spoon.reflect.reference.CtTypeReference; @@ -253,4 +254,16 @@ List getAnnotatedChildren( * Returns the metadata keys stored in an element. */ Set getMetadataKeys(); + + E setComments(List comments); + + /** + * The list of comments + * @return the list of comment + */ + List getComments(); + + E addComment(CtComment comment); + + E removeComment(CtComment comment); } diff --git a/src/main/java/spoon/reflect/factory/CoreFactory.java b/src/main/java/spoon/reflect/factory/CoreFactory.java index 61b23bec148..16c32597696 100644 --- a/src/main/java/spoon/reflect/factory/CoreFactory.java +++ b/src/main/java/spoon/reflect/factory/CoreFactory.java @@ -29,6 +29,7 @@ import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -443,6 +444,11 @@ SourcePosition createSourcePosition( */ CtCodeSnippetStatement createCodeSnippetStatement(); + /** + * Creates a comment. + */ + CtComment createComment(); + /** * Gets the main factory of that core factory (cannot be null). */ diff --git a/src/main/java/spoon/reflect/visitor/CtAbstractVisitor.java b/src/main/java/spoon/reflect/visitor/CtAbstractVisitor.java index 429024fc482..bb9d5ee3b89 100644 --- a/src/main/java/spoon/reflect/visitor/CtAbstractVisitor.java +++ b/src/main/java/spoon/reflect/visitor/CtAbstractVisitor.java @@ -16,8 +16,6 @@ */ package spoon.reflect.visitor; -import java.lang.annotation.Annotation; - import spoon.reflect.code.CtAnnotationFieldAccess; import spoon.reflect.code.CtArrayRead; import spoon.reflect.code.CtArrayWrite; @@ -31,6 +29,7 @@ import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -90,6 +89,8 @@ import spoon.reflect.reference.CtTypeReference; import spoon.reflect.reference.CtUnboundVariableReference; +import java.lang.annotation.Annotation; + /** Provides an empty implementation of CtVIsitor. * See {@link CtScanner} for a much more powerful implementation of CtVisitor. */ @@ -447,4 +448,9 @@ public void visitCtFieldWrite(CtFieldWrite fieldWrite) { public void visitCtSuperAccess(CtSuperAccess f) { } + + @Override + public void visitCtComment(CtComment comment) { + + } } diff --git a/src/main/java/spoon/reflect/visitor/CtInheritanceScanner.java b/src/main/java/spoon/reflect/visitor/CtInheritanceScanner.java index 6a2ccc1d908..5e320a01ed5 100644 --- a/src/main/java/spoon/reflect/visitor/CtInheritanceScanner.java +++ b/src/main/java/spoon/reflect/visitor/CtInheritanceScanner.java @@ -33,6 +33,7 @@ import spoon.reflect.code.CtCodeElement; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -834,6 +835,14 @@ public void visitCtVariableWrite(CtVariableWrite e) { scanCtVisitable(e); } + @Override + public void visitCtComment(CtComment e) { + scanCtElement(e); + scanCtVisitable(e); + scanCtStatement(e); + scanCtCodeElement(e); + } + public void visitCtAnnotationFieldAccess( CtAnnotationFieldAccess e) { visitCtVariableRead(e); diff --git a/src/main/java/spoon/reflect/visitor/CtScanner.java b/src/main/java/spoon/reflect/visitor/CtScanner.java index 6a13bd8f050..dbed9e2b7b8 100644 --- a/src/main/java/spoon/reflect/visitor/CtScanner.java +++ b/src/main/java/spoon/reflect/visitor/CtScanner.java @@ -30,6 +30,7 @@ import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -181,6 +182,7 @@ public void visitCtAnnotationType( scan(annotationType.getAnnotations()); scan(annotationType.getNestedTypes()); scan(annotationType.getFields()); + scan(annotationType.getComments()); exit(annotationType); } @@ -188,6 +190,7 @@ public void visitCtAnonymousExecutable(CtAnonymousExecutable anonymousExec) { enter(anonymousExec); scan(anonymousExec.getAnnotations()); scan(anonymousExec.getBody()); + scan(anonymousExec.getComments()); exit(anonymousExec); } @@ -198,6 +201,7 @@ public > void visitCtArrayAccess(CtArrayAccess void visitCtArrayRead(CtArrayRead arrayRead) { scan(arrayRead.getTypeCasts()); scan(arrayRead.getTarget()); scan(arrayRead.getIndexExpression()); + scan(arrayRead.getComments()); exit(arrayRead); } @@ -220,6 +225,7 @@ public void visitCtArrayWrite(CtArrayWrite arrayWrite) { scan(arrayWrite.getTypeCasts()); scan(arrayWrite.getTarget()); scan(arrayWrite.getIndexExpression()); + scan(arrayWrite.getComments()); exit(arrayWrite); } @@ -243,6 +249,7 @@ public void visitCtAssert(CtAssert asserted) { scan(asserted.getAnnotations()); scan(asserted.getAssertExpression()); scan(asserted.getExpression()); + scan(asserted.getComments()); exit(asserted); } @@ -254,6 +261,7 @@ public void visitCtAssignment( scan(assignement.getTypeCasts()); scan(assignement.getAssigned()); scan(assignement.getAssignment()); + scan(assignement.getComments()); exit(assignement); } @@ -264,6 +272,7 @@ public void visitCtBinaryOperator(CtBinaryOperator operator) { scan(operator.getTypeCasts()); scan(operator.getLeftHandOperand()); scan(operator.getRightHandOperand()); + scan(operator.getComments()); exit(operator); } @@ -271,12 +280,14 @@ public void visitCtBlock(CtBlock block) { enter(block); scan(block.getAnnotations()); scan(block.getStatements()); + scan(block.getComments()); exit(block); } public void visitCtBreak(CtBreak breakStatement) { enter(breakStatement); scan(breakStatement.getAnnotations()); + scan(breakStatement.getComments()); exit(breakStatement); } @@ -285,6 +296,7 @@ public void visitCtCase(CtCase caseStatement) { scan(caseStatement.getAnnotations()); scan(caseStatement.getCaseExpression()); scan(caseStatement.getStatements()); + scan(caseStatement.getComments()); exit(caseStatement); } @@ -293,6 +305,7 @@ public void visitCtCatch(CtCatch catchBlock) { scan(catchBlock.getAnnotations()); scan(catchBlock.getParameter()); scan(catchBlock.getBody()); + scan(catchBlock.getComments()); exit(catchBlock); } @@ -307,6 +320,7 @@ public void visitCtClass(CtClass ctClass) { scan(ctClass.getFields()); scan(ctClass.getConstructors()); scan(ctClass.getMethods()); + scan(ctClass.getComments()); exit(ctClass); } @@ -316,6 +330,7 @@ public void visitCtConditional(CtConditional conditional) { scan(conditional.getCondition()); scan(conditional.getThenExpression()); scan(conditional.getElseExpression()); + scan(conditional.getComments()); exit(conditional); } @@ -326,6 +341,7 @@ public void visitCtConstructor(CtConstructor c) { scan(c.getThrownTypes()); scan(c.getFormalTypeParameters()); scan(c.getBody()); + scan(c.getComments()); exit(c); } @@ -333,6 +349,7 @@ public void visitCtContinue(CtContinue continueStatement) { enter(continueStatement); scan(continueStatement.getAnnotations()); scan(continueStatement.getLabelledStatement()); + scan(continueStatement.getComments()); exit(continueStatement); } @@ -341,6 +358,7 @@ public void visitCtDo(CtDo doLoop) { scan(doLoop.getAnnotations()); scan(doLoop.getLoopingExpression()); scan(doLoop.getBody()); + scan(doLoop.getComments()); exit(doLoop); } @@ -352,6 +370,7 @@ public > void visitCtEnum(CtEnum ctEnum) { scan(ctEnum.getConstructors()); scan(ctEnum.getMethods()); scan(ctEnum.getNestedTypes()); + scan(ctEnum.getComments()); exit(ctEnum); } @@ -362,6 +381,7 @@ public void visitCtExecutableReference( scan(reference.getType()); scan(reference.getActualTypeArguments()); scan(reference.getAnnotations()); + scan(reference.getComments()); exit(reference); } @@ -370,6 +390,7 @@ public void visitCtField(CtField f) { scan(f.getAnnotations()); scan(f.getType()); scan(f.getDefaultExpression()); + scan(f.getComments()); exit(f); } @@ -379,6 +400,7 @@ public void visitCtEnumValue(CtEnumValue enumValue) { scan(enumValue.getAnnotations()); scan(enumValue.getType()); scan(enumValue.getDefaultExpression()); + scan(enumValue.getComments()); exit(enumValue); } @@ -388,6 +410,7 @@ public void visitCtThisAccess(CtThisAccess thisAccess) { scan(thisAccess.getType()); scan(thisAccess.getTypeCasts()); scan(thisAccess.getTarget()); + scan(thisAccess.getComments()); exit(thisAccess); } @@ -399,6 +422,7 @@ public void visitCtAnnotationFieldAccess( scan(annotationFieldAccess.getTypeCasts()); scan(annotationFieldAccess.getTarget()); scan(annotationFieldAccess.getVariable()); + scan(annotationFieldAccess.getComments()); exit(annotationFieldAccess); } @@ -417,6 +441,7 @@ public void visitCtFor(CtFor forLoop) { scan(forLoop.getExpression()); scan(forLoop.getForUpdate()); scan(forLoop.getBody()); + scan(forLoop.getComments()); exit(forLoop); } @@ -426,6 +451,7 @@ public void visitCtForEach(CtForEach foreach) { scan(foreach.getVariable()); scan(foreach.getExpression()); scan(foreach.getBody()); + scan(foreach.getComments()); exit(foreach); } @@ -435,6 +461,7 @@ public void visitCtIf(CtIf ifElement) { scan(ifElement.getCondition()); scan((CtStatement) ifElement.getThenStatement()); scan((CtStatement) ifElement.getElseStatement()); + scan(ifElement.getComments()); exit(ifElement); } @@ -446,6 +473,7 @@ public void visitCtInterface(CtInterface intrface) { scan(intrface.getNestedTypes()); scan(intrface.getFields()); scan(intrface.getMethods()); + scan(intrface.getComments()); exit(intrface); } @@ -456,6 +484,7 @@ public void visitCtInvocation(CtInvocation invocation) { scan(invocation.getTarget()); scan(invocation.getExecutable()); scan(invocation.getArguments()); + scan(invocation.getComments()); exit(invocation); } @@ -465,6 +494,7 @@ public void visitCtLiteral(CtLiteral literal) { scan(literal.getType()); scan(literal.getValue()); scan(literal.getTypeCasts()); + scan(literal.getComments()); exit(literal); } @@ -473,6 +503,7 @@ public void visitCtLocalVariable(CtLocalVariable localVariable) { scan(localVariable.getAnnotations()); scan(localVariable.getType()); scan(localVariable.getDefaultExpression()); + scan(localVariable.getComments()); exit(localVariable); } @@ -488,6 +519,7 @@ public void visitCtCatchVariable(CtCatchVariable catchVariable) { enter(catchVariable); scan(catchVariable.getAnnotations()); scan(catchVariable.getType()); + scan(catchVariable.getComments()); exit(catchVariable); } @@ -506,6 +538,7 @@ public void visitCtMethod(CtMethod m) { scan(m.getParameters()); scan(m.getThrownTypes()); scan(m.getBody()); + scan(m.getComments()); exit(m); } @@ -516,6 +549,7 @@ public void visitCtNewArray(CtNewArray newArray) { scan(newArray.getTypeCasts()); scan(newArray.getElements()); scan(newArray.getDimensionExpressions()); + scan(newArray.getComments()); exit(newArray); } @@ -527,6 +561,7 @@ public void visitCtConstructorCall(CtConstructorCall ctConstructorCall) { scan(ctConstructorCall.getExecutable()); scan(ctConstructorCall.getTarget()); scan(ctConstructorCall.getArguments()); + scan(ctConstructorCall.getComments()); exit(ctConstructorCall); } @@ -539,6 +574,7 @@ public void visitCtNewClass(CtNewClass newClass) { scan(newClass.getTarget()); scan(newClass.getArguments()); scan(newClass.getAnonymousClass()); + scan(newClass.getComments()); exit(newClass); } @@ -551,6 +587,7 @@ public void visitCtLambda(CtLambda lambda) { scan(lambda.getParameters()); scan(lambda.getBody()); scan(lambda.getExpression()); + scan(lambda.getComments()); exit(lambda); } @@ -573,6 +610,7 @@ public void visitCtOperatorAssignment( scan(assignment.getTypeCasts()); scan(assignment.getAssigned()); scan(assignment.getAssignment()); + scan(assignment.getComments()); exit(assignment); } @@ -581,6 +619,7 @@ public void visitCtPackage(CtPackage ctPackage) { scan(ctPackage.getAnnotations()); scan(ctPackage.getPackages()); scan(ctPackage.getTypes()); + scan(ctPackage.getComments()); exit(ctPackage); } @@ -593,6 +632,7 @@ public void visitCtParameter(CtParameter parameter) { enter(parameter); scan(parameter.getAnnotations()); scan(parameter.getType()); + scan(parameter.getComments()); exit(parameter); } @@ -607,6 +647,7 @@ public void visitCtReturn(CtReturn returnStatement) { enter(returnStatement); scan(returnStatement.getAnnotations()); scan(returnStatement.getReturnedExpression()); + scan(returnStatement.getComments()); exit(returnStatement); } @@ -614,6 +655,7 @@ public void visitCtStatementList(CtStatementList statements) { enter(statements); scan(statements.getAnnotations()); scan(statements.getStatements()); + scan(statements.getComments()); exit(statements); } @@ -622,6 +664,7 @@ public void visitCtSwitch(CtSwitch switchStatement) { scan(switchStatement.getAnnotations()); scan(switchStatement.getSelector()); scan(switchStatement.getCases()); + scan(switchStatement.getComments()); exit(switchStatement); } @@ -630,6 +673,7 @@ public void visitCtSynchronized(CtSynchronized synchro) { scan(synchro.getAnnotations()); scan(synchro.getExpression()); scan(synchro.getBlock()); + scan(synchro.getComments()); exit(synchro); } @@ -637,6 +681,7 @@ public void visitCtThrow(CtThrow throwStatement) { enter(throwStatement); scan(throwStatement.getAnnotations()); scan(throwStatement.getThrownExpression()); + scan(throwStatement.getComments()); exit(throwStatement); } @@ -646,6 +691,7 @@ public void visitCtTry(CtTry tryBlock) { scan(tryBlock.getBody()); scan(tryBlock.getCatchers()); scan(tryBlock.getFinalizer()); + scan(tryBlock.getComments()); exit(tryBlock); } @@ -657,6 +703,7 @@ public void visitCtTryWithResource(CtTryWithResource tryWithResource) { scan(tryWithResource.getBody()); scan(tryWithResource.getCatchers()); scan(tryWithResource.getFinalizer()); + scan(tryWithResource.getComments()); exit(tryWithResource); } @@ -683,6 +730,7 @@ public void visitCtTypeReference(CtTypeReference reference) { scan(reference.getDeclaringType()); scan(reference.getActualTypeArguments()); scan(reference.getAnnotations()); + scan(reference.getComments()); exit(reference); } @@ -704,6 +752,7 @@ public void visitCtTypeAccess(CtTypeAccess typeAccess) { scan(typeAccess.getType()); scan(typeAccess.getTypeCasts()); scan(typeAccess.getAccessedType()); + scan(typeAccess.getComments()); exit(typeAccess); } @@ -713,6 +762,7 @@ public void visitCtUnaryOperator(CtUnaryOperator operator) { scan(operator.getType()); scan(operator.getTypeCasts()); scan(operator.getOperand()); + scan(operator.getComments()); exit(operator); } @@ -723,6 +773,7 @@ public void visitCtVariableRead(CtVariableRead variableRead) { scan(variableRead.getType()); scan(variableRead.getTypeCasts()); scan(variableRead.getVariable()); + scan(variableRead.getComments()); exit(variableRead); } @@ -733,6 +784,7 @@ public void visitCtVariableWrite(CtVariableWrite variableWrite) { scan(variableWrite.getType()); scan(variableWrite.getTypeCasts()); scan(variableWrite.getVariable()); + scan(variableWrite.getComments()); exit(variableWrite); } @@ -741,6 +793,7 @@ public void visitCtWhile(CtWhile whileLoop) { scan(whileLoop.getAnnotations()); scan(whileLoop.getLoopingExpression()); scan(whileLoop.getBody()); + scan(whileLoop.getComments()); exit(whileLoop); } @@ -766,6 +819,7 @@ public void visitCtFieldRead(CtFieldRead fieldRead) { scan(fieldRead.getTypeCasts()); scan(fieldRead.getTarget()); scan(fieldRead.getVariable()); + scan(fieldRead.getComments()); exit(fieldRead); } @@ -776,6 +830,7 @@ public void visitCtFieldWrite(CtFieldWrite fieldWrite) { scan(fieldWrite.getTypeCasts()); scan(fieldWrite.getTarget()); scan(fieldWrite.getVariable()); + scan(fieldWrite.getComments()); exit(fieldWrite); } @@ -786,6 +841,13 @@ public void visitCtSuperAccess(CtSuperAccess f) { scan(f.getType()); scan(f.getTypeCasts()); scan(f.getTarget()); + scan(f.getComments()); exit(f); } + + @Override + public void visitCtComment(CtComment comment) { + enter(comment); + exit(comment); + } } diff --git a/src/main/java/spoon/reflect/visitor/CtVisitor.java b/src/main/java/spoon/reflect/visitor/CtVisitor.java index 3285ed6ff3b..1350e43fd05 100644 --- a/src/main/java/spoon/reflect/visitor/CtVisitor.java +++ b/src/main/java/spoon/reflect/visitor/CtVisitor.java @@ -29,6 +29,7 @@ import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -448,4 +449,9 @@ void visitCtOperatorAssignment( * Visits an access to a super invocation. */ void visitCtSuperAccess(CtSuperAccess f); + + /** + * Visits a comment + */ + void visitCtComment(CtComment comment); } diff --git a/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java b/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java index 962f37061bf..f3113fb201b 100644 --- a/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java +++ b/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java @@ -34,6 +34,7 @@ import spoon.reflect.code.CtCodeElement; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -113,6 +114,7 @@ import spoon.support.visitor.SignaturePrinter; import java.lang.annotation.Annotation; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -267,6 +269,7 @@ protected void enterCtExpression(CtExpression e) { * Enters a statement. */ protected void enterCtStatement(CtStatement s) { + printComment(s, CommentOffset.BEFORE); mapLine(line, s); writeAnnotations(s); if (s.getLabel() != null) { @@ -604,6 +607,7 @@ public void visitCtAnnotationType(CtAnnotationType ann } public void visitCtAnonymousExecutable(CtAnonymousExecutable impl) { + printComment(impl); writeAnnotations(impl); writeModifiers(impl); scan(impl.getBody()); @@ -795,6 +799,7 @@ public void visitCtClass(CtClass ctClass) { lst.addAll(ctClass.getNestedTypes()); lst.addAll(ctClass.getFields()); lst.addAll(ctClass.getMethods()); + lst.addAll(getComments(ctClass, CommentOffset.INSIDE)); if ((ctClass.getSimpleName() == null || ctClass.getSimpleName().isEmpty()) && ctClass.getParent() != null && ctClass.getParent() instanceof CtNewClass) { context.currentThis.push(((CtNewClass) ctClass.getParent()).getType()); @@ -830,6 +835,7 @@ public void visitCtConditional(CtConditional conditional) { } public void visitCtConstructor(CtConstructor c) { + printComment(c); visitCtNamedElement(c); writeModifiers(c); writeFormalTypeParameters(c.getFormalTypeParameters()); @@ -937,6 +943,7 @@ public void visitCtExecutableReference(CtExecutableReference reference) { } public void visitCtField(CtField f) { + printComment(f); visitCtNamedElement(f); writeModifiers(f); scan(f.getType()); @@ -1079,6 +1086,14 @@ public void visitCtSuperAccess(CtSuperAccess f) { exitCtExpression(f); } + @Override + public void visitCtComment(CtComment comment) { + if (!env.isGenerateComments() && context.elementStack.size() > 1) { + return; + } + write(comment.getContent()); + } + public void visitCtAnnotationFieldAccess(CtAnnotationFieldAccess annotationFieldAccess) { enterCtExpression(annotationFieldAccess); if (annotationFieldAccess.getTarget() != null) { @@ -1496,7 +1511,50 @@ public DefaultJavaPrettyPrinter writeThrowsClause(CtExecutable e) { return this; } + private void printComment(CtElement e) { + if (!env.isGenerateComments() || e == null) { + return; + } + for (CtComment comment : e.getComments()) { + scan(comment); + writeln().writeTabs(); + } + } + + private void printComment(CtElement e, CommentOffset offset) { + for (CtComment comment : getComments(e, offset)) { + scan(comment); + writeln().writeTabs(); + } + } + + private List getComments(CtElement e, CommentOffset offset) { + List commentsToPrint = new ArrayList(); + if (!env.isGenerateComments() || e == null) { + return commentsToPrint; + } + for (CtComment comment : e.getComments()) { + if (comment.getPosition() == null || e.getPosition() == null) { + if (offset == CommentOffset.BEFORE) { + commentsToPrint.add(comment); + } + continue; + } + if (offset == CommentOffset.BEFORE && comment.getPosition().getLine() <= e.getPosition().getLine()) { + commentsToPrint.add(comment); + } else if (offset == CommentOffset.AFTER && comment.getPosition().getSourceStart() >= e.getPosition().getSourceEnd()) { + commentsToPrint.add(comment); + } else if (offset == CommentOffset.INSIDE && comment.getPosition().getLine() >= e.getPosition().getLine() && comment.getPosition().getEndLine() <= e.getPosition().getEndLine()) { + commentsToPrint.add(comment); + } + } + return commentsToPrint; + } + + + public void visitCtMethod(CtMethod m) { + printComment(m); visitCtNamedElement(m); writeModifiers(m); if (m.isDefaultMethod()) { @@ -1793,6 +1851,7 @@ public void visitCtReturn(CtReturn returnStatement) { } void visitCtType(CtType type) { + printComment(type, CommentOffset.BEFORE); mapLine(line, type); if (type.isTopLevel()) { context.currentTopLevel = type; @@ -2273,9 +2332,10 @@ protected void writeParameters(Collection> params) { protected void writeStatement(CtStatement e) { scan(e); if (!((e instanceof CtBlock) || (e instanceof CtIf) || (e instanceof CtFor) || (e instanceof CtForEach) || (e instanceof CtWhile) || (e instanceof CtTry) || (e instanceof CtSwitch) - || (e instanceof CtSynchronized) || (e instanceof CtClass))) { + || (e instanceof CtSynchronized) || (e instanceof CtClass) || (e instanceof CtComment))) { write(";"); } + printComment(e, CommentOffset.AFTER); } public void visitCtCodeSnippetExpression(CtCodeSnippetExpression expression) { @@ -2313,3 +2373,9 @@ public void visitCtUnboundVariableReference(CtUnboundVariableReference re write(reference.getSimpleName()); } } + +enum CommentOffset { + BEFORE, + AFTER, + INSIDE +} diff --git a/src/main/java/spoon/support/DefaultCoreFactory.java b/src/main/java/spoon/support/DefaultCoreFactory.java index 7e21e6e167a..1dd6f5f2a6b 100644 --- a/src/main/java/spoon/support/DefaultCoreFactory.java +++ b/src/main/java/spoon/support/DefaultCoreFactory.java @@ -30,6 +30,7 @@ import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -103,6 +104,7 @@ import spoon.support.reflect.code.CtCatchVariableImpl; import spoon.support.reflect.code.CtCodeSnippetExpressionImpl; import spoon.support.reflect.code.CtCodeSnippetStatementImpl; +import spoon.support.reflect.code.CtCommentImpl; import spoon.support.reflect.code.CtConditionalImpl; import spoon.support.reflect.code.CtConstructorCallImpl; import spoon.support.reflect.code.CtContinueImpl; @@ -658,6 +660,12 @@ public CtCodeSnippetStatement createCodeSnippetStatement() { return e; } + public CtComment createComment() { + CtComment e = new CtCommentImpl(); + e.setFactory(getMainFactory()); + return e; + } + public CtWhile createWhile() { CtWhile e = new CtWhileImpl(); e.setFactory(getMainFactory()); diff --git a/src/main/java/spoon/support/StandardEnvironment.java b/src/main/java/spoon/support/StandardEnvironment.java index 1040929fa17..52e1d489627 100644 --- a/src/main/java/spoon/support/StandardEnvironment.java +++ b/src/main/java/spoon/support/StandardEnvironment.java @@ -91,6 +91,8 @@ public class StandardEnvironment implements Serializable, Environment { private boolean generateJavadoc = false; + private boolean generateComments = false; + private Logger logger = Logger.getLogger(StandardEnvironment.class); private Level level = Level.OFF; @@ -493,4 +495,12 @@ public boolean isGenerateJavadoc() { public void setGenerateJavadoc(boolean generateJavadoc) { this.generateJavadoc = generateJavadoc; } + + public boolean isGenerateComments() { + return generateComments; + } + + public void setGenerateComments(boolean generateComments) { + this.generateComments = generateComments; + } } diff --git a/src/main/java/spoon/support/compiler/jdt/JDTBasedSpoonCompiler.java b/src/main/java/spoon/support/compiler/jdt/JDTBasedSpoonCompiler.java index 093eb17fbf2..a118933e62e 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTBasedSpoonCompiler.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTBasedSpoonCompiler.java @@ -429,11 +429,12 @@ protected boolean buildSources(JDTBuilder jdtBuilder) { } } CompilationUnitDeclaration[] units = batchCompiler.getUnits(filesToBuild); - // here we build the model JDTTreeBuilder builder = new JDTTreeBuilder(factory); - for (CompilationUnitDeclaration unit : units) { + for (int i = 0; i < units.length; i++) { + CompilationUnitDeclaration unit = units[i]; unit.traverse(builder, unit.scope); + new JDTCommentBuilder(unit, factory).build(); } return probs.size() == 0; @@ -479,8 +480,10 @@ protected boolean buildTemplates(JDTBuilder jdtBuilder) { // here we build the model in the template factory JDTTreeBuilder builder = new JDTTreeBuilder(factory); - for (CompilationUnitDeclaration unit : units) { + for (int i = 0; i < units.length; i++) { + CompilationUnitDeclaration unit = units[i]; unit.traverse(builder, unit.scope); + new JDTCommentBuilder(unit, factory).build(); } return probs.size() == 0; diff --git a/src/main/java/spoon/support/compiler/jdt/JDTBatchCompiler.java b/src/main/java/spoon/support/compiler/jdt/JDTBatchCompiler.java index b85c1db372c..2f4dbc33a09 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTBatchCompiler.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTBatchCompiler.java @@ -20,17 +20,23 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import org.apache.commons.io.IOUtils; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.batch.CompilationUnit; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; +import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; +import org.eclipse.jdt.internal.core.util.CommentRecorderParser; import spoon.Launcher; import spoon.compiler.SpoonFile; @@ -135,6 +141,26 @@ this.jdtCompiler.requestor, getProblemFactory(), this.out, null); CompilationUnitDeclaration[] units = treeBuilderCompiler .buildUnits(getCompilationUnits(files)); + for (int i = 0; i < units.length; i++) { + CompilationUnitDeclaration unit = units[i]; + CommentRecorderParser parser = + new CommentRecorderParser( + new ProblemReporter( + DefaultErrorHandlingPolicies.proceedWithAllProblems(), + compilerOptions, + new DefaultProblemFactory(Locale.getDefault())), + false); + ICompilationUnit sourceUnit = + new CompilationUnit( + getCompilationUnits()[i].getContents(), + "", //$NON-NLS-1$ + compilerOptions.defaultEncoding); + final CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, compilerOptions.maxProblemsPerUnit); + CompilationUnitDeclaration compilationUnitDeclaration = parser.dietParse(sourceUnit, compilationResult); + unit.comments = compilationUnitDeclaration.comments; + } + + return units; } diff --git a/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java b/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java new file mode 100644 index 00000000000..1f12e5e4d39 --- /dev/null +++ b/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java @@ -0,0 +1,353 @@ +/** + * Copyright (C) 2006-2015 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.support.compiler.jdt; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import spoon.reflect.code.CtBinaryOperator; +import spoon.reflect.code.CtCase; +import spoon.reflect.code.CtComment; +import spoon.reflect.code.CtNewArray; +import spoon.reflect.code.CtStatement; +import spoon.reflect.code.CtStatementList; +import spoon.reflect.code.CtSwitch; +import spoon.reflect.cu.CompilationUnit; +import spoon.reflect.cu.SourcePosition; +import spoon.reflect.declaration.CtAnonymousExecutable; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtConstructor; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtField; +import spoon.reflect.declaration.CtInterface; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.ParentNotInitializedException; +import spoon.reflect.factory.Factory; +import spoon.reflect.visitor.CtInheritanceScanner; +import spoon.reflect.visitor.CtScanner; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@SuppressWarnings("unchecked") +public class JDTCommentBuilder { + + private final CompilationUnitDeclaration declarationUnit; + private String filePath; + private CompilationUnit spoonUnit; + private Factory factory; + private ICompilationUnit sourceUnit; + private char[] contents; + + public JDTCommentBuilder(CompilationUnitDeclaration declarationUnit, Factory factory) { + this.declarationUnit = declarationUnit; + if (declarationUnit.comments == null) { + return; + } + this.factory = factory; + this.sourceUnit = declarationUnit.compilationResult.compilationUnit; + this.contents = sourceUnit.getContents(); + this.filePath = CharOperation.charToString(sourceUnit.getFileName()); + this.spoonUnit = factory.CompilationUnit().create(filePath); + } + + public void build() { + if (declarationUnit.comments == null) { + return; + } + for (int j = 0; j < declarationUnit.comments.length; j++) { + int[] positions = declarationUnit.comments[j]; + buildComment(positions); + } + } + + private void buildComment(int[] positions) { + int start = positions[0]; + final int end = -positions[1]; + + boolean isInline = false; + // the inline comments have negative start + if (start < 0) { + isInline = true; + start = -start; + } + // Javadoc comments have negative end position + if (end <= 0) { + // The javadoc is already handled by the JDTTreeBuilder + return; + } + String commentContent = getCommentContent(start, end); + + int[] lineSeparatorPositions = declarationUnit.compilationResult.lineSeparatorPositions; + SourcePosition sourcePosition = factory.Core().createSourcePosition(spoonUnit, start, start, end, lineSeparatorPositions); + + CtComment comment = factory.Core().createComment(); + comment.setContent(commentContent); + comment.setInline(isInline); + comment.setPosition(sourcePosition); + + insertCommentInAST(comment); + } + + private CtElement addCommentToNear(final CtComment comment, final Collection 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; + } + if (element.getPosition().getLine() == comment.getPosition().getLine()) { + element.addComment(comment); + return element; + } + int distance = Math.abs(element.getPosition().getSourceStart() - comment.getPosition().getSourceEnd()); + boolean isAfter = element.getPosition().getSourceEnd() < comment.getPosition().getSourceStart(); + if (distance < smallDistance && !isAfter) { + best = element; + smallDistance = distance; + } + } + if (best != null) { + best.addComment(comment); + } + return best; + } + + private void insertCommentInAST(final CtComment comment) { + CtElement commentParent = findCommentParent(comment); + if (commentParent == null) { + addCommentToNear(comment, new ArrayList(spoonUnit.getDeclaredTypes())); + return; + } + 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 void visitCtStatementList(CtStatementList e) { + addCommentToNear(comment, new ArrayList(e.getStatements())); + try { + comment.getParent(); + } catch (ParentNotInitializedException ex) { + e.addStatement(comment); + } + } + + @Override + public void visitCtMethod(CtMethod e) { + e.addComment(comment); + } + + @Override + public void visitCtConstructor(CtConstructor e) { + e.addComment(comment); + } + + @Override + public void visitCtBinaryOperator(CtBinaryOperator e) { + List elements = new ArrayList(); + elements.add(e.getLeftHandOperand()); + elements.add(e.getRightHandOperand()); + addCommentToNear(comment, elements); + } + + @Override + public void visitCtClass(CtClass e) { + List elements = new ArrayList(); + elements.addAll(e.getFields()); + elements.addAll(e.getMethods()); + elements.addAll(e.getConstructors()); + addCommentToNear(comment, elements); + + try { + comment.getParent(); + } catch (ParentNotInitializedException ex) { + e.addComment(comment); + } + } + + @Override + public void visitCtInterface(CtInterface e) { + List elements = new ArrayList(); + elements.addAll(e.getFields()); + elements.addAll(e.getMethods()); + addCommentToNear(comment, elements); + + try { + comment.getParent(); + } catch (ParentNotInitializedException ex) { + e.addComment(comment); + } + } + + @Override + public void visitCtField(CtField e) { + e.addComment(comment); + } + + @Override + public void visitCtSwitch(CtSwitch e) { + List> cases = e.getCases(); + CtCase previous = null; + for (int i = 0; i < cases.size(); i++) { + CtCase 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(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(previous.getStatements())); + try { + comment.getParent(); + } catch (ParentNotInitializedException ex) { + previous.addStatement(comment); + } + return; + } + 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 void visitCtNewArray(CtNewArray e) { + addCommentToNear(comment, new ArrayList(e.getElements())); + try { + comment.getParent(); + } catch (ParentNotInitializedException ex) { + e.addComment(comment); + } + } + }; + insertionVisitor.scan(commentParent); + try { + comment.getParent(); + } catch (ParentNotInitializedException e) { + System.out.println(comment); + System.out.println(comment.getPosition()); + System.out.println(commentParent); + throw e; + } + } + + 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; + } + + @Override + public void scan(CtElement element) { + if (element == null) { + return; + } + CtElement body; + try { + Method getBody = element.getClass().getMethod("getBody"); + body = (CtElement) getBody.invoke(element); + if (body != null) { + if (body.getPosition() == null) { + body = null; + } + } + } catch (Exception e) { + body = null; + } + if (element != null + && element.getPosition() != null + && ((element.getPosition().getSourceStart() <= start + && element.getPosition().getSourceEnd() >= end) + || (body != null && (body.getPosition().getSourceStart() <= start + && body.getPosition().getSourceEnd() >= end)))) { + commentParent = element; + element.accept(this); + } + } + } + FindCommentParentScanner findCommentParentScanner = new FindCommentParentScanner( + comment.getPosition().getSourceStart(), + comment.getPosition().getSourceEnd()); + findCommentParentScanner.scan(spoonUnit.getDeclaredTypes()); + return findCommentParentScanner.commentParent; + } + + private String getCommentContent(int start, int end) { + StringBuilder stringBuilder = new StringBuilder(); + for (int f = start; f < end; f++) { + stringBuilder.append(contents[f]); + } + return stringBuilder.toString().trim(); + } +} diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java index f5790f5b75c..d7572e4847f 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java @@ -170,6 +170,7 @@ import spoon.reflect.code.CtOperatorAssignment; import spoon.reflect.code.CtReturn; import spoon.reflect.code.CtStatement; +import spoon.reflect.code.CtStatementList; import spoon.reflect.code.CtSuperAccess; import spoon.reflect.code.CtSwitch; import spoon.reflect.code.CtSynchronized; @@ -313,7 +314,7 @@ void enter(CtElement e, ASTNode node) { } else if (node instanceof TypeDeclaration) { sourceStartDeclaration = ((TypeDeclaration) node).declarationSourceStart; sourceEnd = ((TypeDeclaration) node).declarationSourceEnd; - } else if ((e instanceof CtBlock) && (node instanceof AbstractMethodDeclaration)) { + } else if ((e instanceof CtStatementList) && (node instanceof AbstractMethodDeclaration)) { sourceStartDeclaration = ((AbstractMethodDeclaration) node).bodyStart - 1; sourceEnd = ((AbstractMethodDeclaration) node).bodyEnd + 1; } else if ((node instanceof AbstractMethodDeclaration)) { diff --git a/src/main/java/spoon/support/reflect/code/CtCommentImpl.java b/src/main/java/spoon/support/reflect/code/CtCommentImpl.java new file mode 100644 index 00000000000..4dd7b1c59a0 --- /dev/null +++ b/src/main/java/spoon/support/reflect/code/CtCommentImpl.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2006-2015 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.support.reflect.code; + +import spoon.reflect.code.CtComment; +import spoon.reflect.visitor.CtVisitor; + +public class CtCommentImpl extends CtStatementImpl implements CtComment { + private static final long serialVersionUID = 1L; + + private String content; + + private boolean isInline; + + @Override + public String getContent() { + return content; + } + + @Override + public E setContent(String content) { + this.content = content; + return (E) this; + } + + @Override + public boolean isInline() { + return isInline; + } + + @Override + public E setInline(boolean inline) { + isInline = inline; + return (E) this; + } + + @Override + public void accept(CtVisitor visitor) { + visitor.visitCtComment(this); + } +} diff --git a/src/main/java/spoon/support/reflect/declaration/CtAnonymousExecutableImpl.java b/src/main/java/spoon/support/reflect/declaration/CtAnonymousExecutableImpl.java index ec93c7152ec..95256b313f1 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtAnonymousExecutableImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtAnonymousExecutableImpl.java @@ -154,7 +154,7 @@ public T setSimpleName(String simpleName) { @Override public CtTypeReference getType() { - return factory.Type().VOID_PRIMITIVE; + return getFactory().Type().VOID_PRIMITIVE; } @Override diff --git a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java index f1a8efc7714..5bae2243a83 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java @@ -16,23 +16,10 @@ */ package spoon.support.reflect.declaration; -import static spoon.reflect.ModelElementContainerDefaultCapacities.ANNOTATIONS_CONTAINER_DEFAULT_CAPACITY; - -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - import org.apache.log4j.Logger; - import spoon.Launcher; import spoon.processing.FactoryAccessor; +import spoon.reflect.code.CtComment; import spoon.reflect.cu.SourcePosition; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtElement; @@ -54,6 +41,19 @@ import spoon.support.visitor.SignaturePrinter; import spoon.support.visitor.TypeReferenceScanner; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static spoon.reflect.ModelElementContainerDefaultCapacities.ANNOTATIONS_CONTAINER_DEFAULT_CAPACITY; + /** * Contains the default implementation of most CtElement methods. * @@ -64,6 +64,8 @@ public abstract class CtElementImpl implements CtElement, Serializable, Comparab protected static final Logger LOGGER = Logger.getLogger(CtElementImpl.class); public static final String ERROR_MESSAGE_TO_STRING = "Error in printing the node. One parent isn't initialized!"; + private List comments = new ArrayList(); + // we don't use Collections.unmodifiableList and Collections.unmodifiableSet // because we need clear() for all set* methods // and UnmodifiableList and unmodifiableCollection are not overridable (not visible grrrr) @@ -496,4 +498,32 @@ public Object getMetadata(String key) { public Set getMetadataKeys() { return metadata.keySet(); } + + + @Override + public List getComments() { + return Collections.unmodifiableList(comments); + } + + @Override + public E addComment(CtComment comment) { + comments.add(comment); + comment.setParent(this); + return (E) this; + } + + @Override + public E removeComment(CtComment comment) { + comments.remove(comment); + return (E) this; + } + + @Override + public E setComments(List comments) { + this.comments = new ArrayList(); + for (CtComment comment : comments) { + this.addComment(comment); + } + return (E) this; + } } diff --git a/src/main/java/spoon/support/reflect/eval/VisitorPartialEvaluator.java b/src/main/java/spoon/support/reflect/eval/VisitorPartialEvaluator.java index e761ffb5441..adfbe1f77b4 100644 --- a/src/main/java/spoon/support/reflect/eval/VisitorPartialEvaluator.java +++ b/src/main/java/spoon/support/reflect/eval/VisitorPartialEvaluator.java @@ -32,6 +32,7 @@ import spoon.reflect.code.CtCodeElement; import spoon.reflect.code.CtCodeSnippetExpression; import spoon.reflect.code.CtCodeSnippetStatement; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -742,6 +743,11 @@ public void visitCtImplicitTypeReference(CtImplicitTypeReference referenc throw new RuntimeException("Unknow Element"); } + @Override + public void visitCtComment(CtComment comment) { + throw new RuntimeException("Unknow Element"); + } + @Override public void visitCtTypeAccess(CtTypeAccess typeAccess) { throw new RuntimeException("Unknown Element"); diff --git a/src/main/java/spoon/template/TemplateMatcher.java b/src/main/java/spoon/template/TemplateMatcher.java index a658ea6a65e..b596ce51de1 100644 --- a/src/main/java/spoon/template/TemplateMatcher.java +++ b/src/main/java/spoon/template/TemplateMatcher.java @@ -16,17 +16,6 @@ */ package spoon.template; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import spoon.Launcher; import spoon.reflect.code.CtFieldAccess; import spoon.reflect.code.CtInvocation; @@ -53,6 +42,17 @@ import spoon.support.template.Parameters; import spoon.support.util.RtHelper; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * This class defines an engine for matching a template to pieces of code. */ @@ -365,6 +365,12 @@ private boolean helperMatch(Object target, Object template) { if (f.getName().equals("factory")) { continue; } + if (f.getName().equals("comments")) { + continue; + } + if (f.getName().equals("metadata")) { + continue; + } try { if (!helperMatch(f.get(target), f.get(template))) { return false; diff --git a/src/test/java/spoon/test/comment/CommentTest.java b/src/test/java/spoon/test/comment/CommentTest.java new file mode 100644 index 00000000000..53b3eca5f38 --- /dev/null +++ b/src/test/java/spoon/test/comment/CommentTest.java @@ -0,0 +1,175 @@ +package spoon.test.comment; + +import org.junit.Test; +import spoon.Launcher; +import spoon.reflect.code.CtComment; +import spoon.reflect.code.CtConstructorCall; +import spoon.reflect.code.CtDo; +import spoon.reflect.code.CtFor; +import spoon.reflect.code.CtIf; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.code.CtLocalVariable; +import spoon.reflect.code.CtReturn; +import spoon.reflect.code.CtSwitch; +import spoon.reflect.code.CtSynchronized; +import spoon.reflect.code.CtTry; +import spoon.reflect.declaration.CtAnonymousExecutable; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtConstructor; +import spoon.reflect.declaration.CtField; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.factory.Factory; +import spoon.reflect.visitor.filter.TypeFilter; +import spoon.test.comment.testclasses.InlineComment; + +import static org.junit.Assert.assertEquals; + +public class CommentTest { + + private String newLine = System.getProperty("line.separator"); + + private Factory getSpoonFacotry() throws Exception { + final Launcher launcher = new Launcher(); + launcher.run(new String[] { + "-i", "./src/test/java/spoon/test/comment/testclasses/", + "-o", "./target/spooned/" + }); + launcher.getEnvironment().setGenerateComments(true); + return launcher.getFactory(); + } + + private CtComment createFakeComment(Factory factory, String content) { + return factory.Core().createComment().setContent(content).setInline(true); + } + + @Test + public void testInLineComment() throws Exception { + Factory f = getSpoonFacotry(); + CtClass type = (CtClass) f.Type().get(InlineComment.class); + + assertEquals(33, type.getElements(new TypeFilter(CtComment.class)).size()); + + assertEquals(3, type.getComments().size()); + assertEquals(createFakeComment(f, "// comment class"), type.getComments().get(1)); + + CtField field = type.getField("field"); + assertEquals(2, field.getComments().size()); + assertEquals(createFakeComment(f, "// Comment Field"), field.getComments().get(0)); + assertEquals("// Comment Field" + 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 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 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 + + "spoon.test.comment.testclasses.InlineComment.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 local variable"), 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 + + "} 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(spoon.test.comment.testclasses.InlineComment.this) {" + newLine + + " // comment in synchronized" + newLine + + "}", ctSynchronized.toString()); + + CtReturn ctReturn = m1.getBody().getStatement(10); + assertEquals(createFakeComment(f, "// comment return"), ctReturn.getComments().get(0)); + assertEquals("// comment return" + newLine + + "return ", ctReturn.toString()); + + + } +} diff --git a/src/test/java/spoon/test/comment/testclasses/InlineComment.java b/src/test/java/spoon/test/comment/testclasses/InlineComment.java new file mode 100644 index 00000000000..eb5238ef030 --- /dev/null +++ b/src/test/java/spoon/test/comment/testclasses/InlineComment.java @@ -0,0 +1,82 @@ +/* + * Top File + */ +package spoon.test.comment.testclasses; + +// comment class +public class InlineComment { + // Comment Field + private int field // comment in field + = 10; + + // comment static block + static { + // comment inside static + } + + // comment constructor + public InlineComment() { + // Comment in constructor + } + + // comment method + public void m() { + // comment empty method block + } + + public void m1() { + // comment switch + switch (1) { + // before first case + case 0: + // comment case 0: empty case + case 1: + // comment case 1 + int i = 0; + default: + // comment default + } + // comment for + for (int i = 0; i < 10; i++) { + // comment for block + } + // comment if + if (1 % 2 == 0) { + // comment unary operator + field++; + } + // comment constructor call + new InlineComment(); + + // comment invocation + this.m(); + + // comment local variable + int i = 0, + // comment multi assignments + j = 2; + // comment dowhile + do { + // comment in do while + i++; + // comment end do while + } while (i < 10); + + // comment try + try { + // comment in try + i++; + } catch (Exception e) { + // comment in catch + } + // comment synchronized + synchronized (this) { + // comment in synchronized + } + + // comment return + return; + } + + // comment after class +} diff --git a/src/test/java/spoon/test/prettyprinter/LinesTest.java b/src/test/java/spoon/test/prettyprinter/LinesTest.java index 5c231a2ca33..b4ed0e8985e 100644 --- a/src/test/java/spoon/test/prettyprinter/LinesTest.java +++ b/src/test/java/spoon/test/prettyprinter/LinesTest.java @@ -1,16 +1,15 @@ package spoon.test.prettyprinter; -import static org.junit.Assert.assertEquals; - import org.junit.Before; import org.junit.Test; - import spoon.Launcher; import spoon.compiler.SpoonResourceHelper; import spoon.reflect.declaration.CtType; import spoon.reflect.factory.Factory; import spoon.reflect.visitor.DefaultJavaPrettyPrinter; +import static org.junit.Assert.assertEquals; + public class LinesTest { Factory factory; @@ -26,6 +25,7 @@ public void setup() throws Exception { .build(); factory.getEnvironment().setPreserveLineNumbers(true); factory.getEnvironment().setAutoImports(false); + factory.getEnvironment().setLevel("DEBUG"); } @Test