From 8d6551fc551c21c7ed1c56b731a9292a79041b00 Mon Sep 17 00:00:00 2001 From: tdurieux Date: Wed, 9 Mar 2016 10:17:25 +0100 Subject: [PATCH] feat(comment): Adds comment support --- .travis.yml | 2 + doc/_jekyll/_data/sidebar_doc.yml | 7 + doc/comments.md | 69 +++ .../java/spoon/reflect/code/CtComment.java | 47 ++ .../spoon/reflect/declaration/CtElement.java | 27 ++ .../spoon/reflect/factory/CodeFactory.java | 22 + .../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 | 167 ++++++- .../spoon/support/DefaultCoreFactory.java | 10 + .../compiler/jdt/JDTBasedSpoonCompiler.java | 9 +- .../compiler/jdt/JDTBatchCompiler.java | 26 + .../compiler/jdt/JDTCommentBuilder.java | 458 ++++++++++++++++++ .../support/compiler/jdt/JDTTreeBuilder.java | 71 +-- .../support/reflect/code/CtCommentImpl.java | 87 ++++ .../reflect/declaration/CtElementImpl.java | 81 +++- .../reflect/eval/VisitorPartialEvaluator.java | 6 + .../java/spoon/template/TemplateMatcher.java | 28 +- .../java/spoon/test/comment/CommentTest.java | 450 +++++++++++++++++ .../comment/testclasses/BlockComment.java | 102 ++++ .../comment/testclasses/InlineComment.java | 117 +++++ .../java/spoon/test/javadoc/JavaDocTest.java | 2 +- .../spoon/test/prettyprinter/LinesTest.java | 5 +- 26 files changed, 1761 insertions(+), 125 deletions(-) create mode 100644 doc/comments.md 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/BlockComment.java create mode 100644 src/test/java/spoon/test/comment/testclasses/InlineComment.java diff --git a/.travis.yml b/.travis.yml index 6e0d1ae07db..79edd061fa4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: java +sudo: true + jdk: - oraclejdk8 diff --git a/doc/_jekyll/_data/sidebar_doc.yml b/doc/_jekyll/_data/sidebar_doc.yml index 9020d15dd8c..44053f6365f 100755 --- a/doc/_jekyll/_data/sidebar_doc.yml +++ b/doc/_jekyll/_data/sidebar_doc.yml @@ -95,6 +95,13 @@ entries: product: all version: all + - title: Comments + url: /comments.html + audience: writers, designers + platform: all + product: all + version: all + - title: Querying source code elements audience: writers, designers platform: all diff --git a/doc/comments.md b/doc/comments.md new file mode 100644 index 00000000000..195a005dc42 --- /dev/null +++ b/doc/comments.md @@ -0,0 +1,69 @@ +--- +title: Comments +keywords: comments +last_updated: Mars 22, 2016 +--- + +### Comments in Spoon + +In Spoon there are different kinds of comments: + +* Line comments (from // to end line) +* Block comments (from /* to */) +* Javadoc comments (from /** to */) + +#### Javadoc Comments + +The Javadoc comments are available via the API ```CtElement.getDocComment()``` and return a ```String```. + +#### Other Comments + +The block and line comments are represented with a ```CtComment``` class ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/declaration/CtComment.html)). +This class exposes API to get the position and the content of the comment. + +We also try to understand to which element they are attached. +We use simple heuristics that work well in nominal cases but it is not possible to address all specific cases. +You can receive the comments of each ```CtElement``` via the API ```CtElement.getComments()``` that returns a ```List```. + +The reprint of the comments can be disable in the Environment. + +##### Comment Attribution + +* Each comment can have multiple comments +* Comments in the same line of a statement is attached to the statement +* Comments which are alone in one line (or more than one lines) are associated to the first element following them. +* Comments cannot be associated to other comment +* Comments at the end of a block are considered as orphans comment +* Comments in a class level is attached to the class + +##### Comment Example +Class comment +```Java +// class comment +class A { + // class comment +} +``` + +Statement comment +```Java +// Statement comment +int a; // Statement comment +``` + +Orphan comment +```Java +try { + +} exception (Exception e) { + // Orphan comment +} +``` + +Multiple line comment +```Java +// Statement comment 1 +// Statement comment 2 +// Statement comment 3 +int a; +``` \ No newline at end of file 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..f42daf41dc6 --- /dev/null +++ b/src/main/java/spoon/reflect/code/CtComment.java @@ -0,0 +1,47 @@ +/** + * 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; + +/** + * This code element defines a comment + * + */ +public interface CtComment extends CtStatement { + enum CommentType { + FILE_COMMENT, // comment at the top of a file + JAVADOC, // /** */ + INLINE, // // + BLOCK // /* */ + } + + /** + * Get the content of the comment + * @return the content of the comment + */ + String getContent(); + + E setContent(String content); + + /** + * Get the type of the comment + * @return the comment type + */ + CommentType getCommentType(); + + + E setCommentType(CommentType commentType); +} diff --git a/src/main/java/spoon/reflect/declaration/CtElement.java b/src/main/java/spoon/reflect/declaration/CtElement.java index 56332d30e93..4b8e24dd8c6 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; @@ -76,6 +77,7 @@ CtAnnotation getAnnotation( * Returns the text of the documentation ("javadoc") comment of this * element. */ + @Deprecated String getDocComment(); @@ -121,6 +123,7 @@ CtAnnotation getAnnotation( * Sets the text of the documentation ("javadoc") comment of this * declaration. */ + @Deprecated E setDocComment(String docComment); /** @@ -253,4 +256,28 @@ List getAnnotatedChildren( * Returns the metadata keys stored in an element. */ Set getMetadataKeys(); + + /** + * Set the comment list + */ + E setComments(List comments); + + /** + * The list of comments + * @return the list of comment + */ + List getComments(); + + /** + * Add a comment to the current element + * element.addComment(element.getFactory().Code().createComment("comment", CtComment.CommentType.INLINE) + * @param comment the comment + */ + E addComment(CtComment comment); + + /** + * Remove a comment + * @param comment the comment to remove + */ + E removeComment(CtComment comment); } diff --git a/src/main/java/spoon/reflect/factory/CodeFactory.java b/src/main/java/spoon/reflect/factory/CodeFactory.java index ab062e3f8a7..c9a19c799ab 100644 --- a/src/main/java/spoon/reflect/factory/CodeFactory.java +++ b/src/main/java/spoon/reflect/factory/CodeFactory.java @@ -24,6 +24,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.CtConstructorCall; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtFieldAccess; @@ -531,4 +532,25 @@ public CtCodeSnippetStatement createCodeSnippetStatement(String statement) { e.setValue(statement); return e; } + + /** + * Creates a comment + * + * @param content The content of the comment + * @param type The comment type + * @return a new CtComment + */ + public CtComment createComment(String content, CtComment.CommentType type) { + return factory.Core().createComment().setContent(content).setCommentType(type); + } + + /** + * Creates an inline comment + * + * @param content The content of the comment + * @return a new CtComment + */ + public CtComment createInlineComment(String content) { + return createComment(content, CtComment.CommentType.INLINE); + } } 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 87d9d0b12dc..82b977c49f9 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,57 @@ public void visitCtSuperAccess(CtSuperAccess f) { exitCtExpression(f); } + @Override + public void visitCtComment(CtComment comment) { + if (!env.isGenerateJavadoc() && context.elementStack.size() > 1) { + return; + } + switch (comment.getCommentType()) { + case FILE_COMMENT: + case JAVADOC: + write("/**").writeln().writeTabs(); + break; + case INLINE: + write("// "); + break; + case BLOCK: + write("/* "); + break; + } + String content = comment.getContent(); + switch (comment.getCommentType()) { + case FILE_COMMENT: + case JAVADOC: + case BLOCK: + String[] lines = content.split("\n"); + for (int i = 0; i < lines.length; i++) { + String com = lines[i]; + if ("".equals(com) && (i == 0 || i == lines.length - 1)) { + continue; + } + if (comment.getCommentType() == CtComment.CommentType.BLOCK) { + write(com); + if (lines.length > 1) { + writeln().writeTabs(); + } + } else { + write(" * " + com).writeln().writeTabs(); + } + + } + break; + default: + write(content); + } + + switch (comment.getCommentType()) { + case BLOCK: + case FILE_COMMENT: + case JAVADOC: + write(" */"); + } + } + public void visitCtAnnotationFieldAccess(CtAnnotationFieldAccess annotationFieldAccess) { enterCtExpression(annotationFieldAccess); if (annotationFieldAccess.getTarget() != null) { @@ -1115,7 +1173,7 @@ public void visitCtFieldReference(CtFieldReference reference) { } else { ref2 = context.currentTopLevel.getReference(); } - // print type if not annonymous class ref and not within the + // print type if not anonymous class ref and not within the // current scope printType = !"".equals(declTypeRef.getSimpleName()) && !(declTypeRef.equals(ref2)); } else { @@ -1197,6 +1255,12 @@ public void visitCtIf(CtIf ifElement) { write(" "); } else { incTab().writeln().writeTabs(); + List comments = getComments(ifElement, CommentOffset.INSIDE); + for (CtComment comment : comments) { + if (comment.getPosition().getSourceStart() <= ifElement.getThenStatement().getPosition().getSourceStart()) { + printComment(comment); + } + } writeStatement(ifElement.getThenStatement()); if (env.isPreserveLineNumbers()) { decTab(); @@ -1205,6 +1269,12 @@ public void visitCtIf(CtIf ifElement) { } } if (ifElement.getElseStatement() != null) { + List comments = getComments(ifElement, CommentOffset.INSIDE); + for (CtComment comment : comments) { + if (comment.getPosition().getSourceStart() > ifElement.getThenStatement().getPosition().getSourceEnd()) { + printComment(comment); + } + } write("else"); if (ifElement.getElseStatement() instanceof CtIf) { write(" "); @@ -1496,7 +1566,68 @@ public DefaultJavaPrettyPrinter writeThrowsClause(CtExecutable e) { return this; } + private void printComment(CtComment comment) { + if (!env.isGenerateJavadoc() || comment == null) { + return; + } + scan(comment); + writeln().writeTabs(); + } + + private void printComment(List comments) { + if (!env.isGenerateJavadoc() || comments == null) { + return; + } + for (CtComment comment : comments) { + printComment(comment); + } + } + + private void printComment(CtElement e) { + if (e == null) { + return; + } + printComment(e.getComments()); + } + + private void printComment(CtElement e, CommentOffset offset) { + printComment(getComments(e, offset)); + } + + private List getComments(CtElement e, CommentOffset offset) { + List commentsToPrint = new ArrayList(); + if (!env.isGenerateJavadoc() || e == null) { + return commentsToPrint; + } + for (CtComment comment : e.getComments()) { + if (comment.getCommentType() == CtComment.CommentType.FILE_COMMENT && offset == CommentOffset.TOP_FILE) { + commentsToPrint.add(comment); + continue; + } + if (comment.getCommentType() == CtComment.CommentType.FILE_COMMENT) { + continue; + } + 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()) { @@ -1544,23 +1675,6 @@ public DefaultJavaPrettyPrinter writeModifiers(CtModifiable m) { } public void visitCtNamedElement(CtNamedElement e) { - // Write comments - if (!env.isPreserveLineNumbers() && env.isGenerateJavadoc() && (e.getDocComment() != null)) { - write("/** ").writeln().writeTabs(); - String[] lines = e.getDocComment().split("\n"); - for (int i = 0; i < lines.length; i++) { - String com = lines[i]; - if ("".equals(com) && (i == 0 || i == lines.length - 1)) { - continue; - } - if (com.startsWith("//")) { - write(com).writeln().writeTabs(); - } else { - write(" * " + com).writeln().writeTabs(); - } - } - write(" */").writeln().writeTabs(); - } // Write element parameters (Annotations) writeAnnotations(e); if (env.isPreserveLineNumbers()) { @@ -1770,6 +1884,7 @@ public void visitCtPackageReference(CtPackageReference reference) { } public void visitCtParameter(CtParameter parameter) { + printComment(parameter); writeAnnotations(parameter); writeModifiers(parameter); if (parameter.isVarArgs()) { @@ -1793,6 +1908,7 @@ public void visitCtReturn(CtReturn returnStatement) { } void visitCtType(CtType type) { + printComment(type, CommentOffset.BEFORE); mapLine(line, type); if (type.isTopLevel()) { context.currentTopLevel = type; @@ -2140,6 +2256,11 @@ public DefaultJavaPrettyPrinter writeActualTypeArguments(CtGenericElementReferen */ public DefaultJavaPrettyPrinter writeHeader(List> types, Collection> imports) { if (!types.isEmpty()) { + for (int i = 0; i < types.size(); i++) { + CtType ctType = types.get(i); + printComment(ctType, CommentOffset.TOP_FILE); + writeln().writeln().writeTabs(); + } CtPackage pack = types.get(0).getPackage(); scan(pack).writeln().writeln().writeTabs(); if (env.isAutoImports()) { @@ -2273,9 +2394,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 +2435,10 @@ public void visitCtUnboundVariableReference(CtUnboundVariableReference re write(reference.getSimpleName()); } } + +enum CommentOffset { + TOP_FILE, + BEFORE, + AFTER, + INSIDE +} diff --git a/src/main/java/spoon/support/DefaultCoreFactory.java b/src/main/java/spoon/support/DefaultCoreFactory.java index 7e21e6e167a..6a97b0f4206 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,14 @@ public CtCodeSnippetStatement createCodeSnippetStatement() { return e; } + public CtComment createComment() { + CtComment e = new CtCommentImpl(); + e.setCommentType(CtComment.CommentType.BLOCK); + e.setContent(""); + e.setFactory(getMainFactory()); + return e; + } + public CtWhile createWhile() { CtWhile e = new CtWhileImpl(); e.setFactory(getMainFactory()); 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 f12d960b1b6..5b54fafc222 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTBatchCompiler.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTBatchCompiler.java @@ -19,11 +19,16 @@ 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; import spoon.reflect.declaration.CtPackage; @@ -37,6 +42,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; // we use a fully qualified name to make it clear we are extending jdt @@ -189,6 +195,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..7fa90ed8407 --- /dev/null +++ b/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java @@ -0,0 +1,458 @@ +/** + * 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.apache.log4j.Logger; +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.CtBlock; +import spoon.reflect.code.CtCase; +import spoon.reflect.code.CtComment; +import spoon.reflect.code.CtIf; +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.CtParameter; +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; + +/** + * The comment builder that will insert all element of a CompilationUnitDeclaration into the Spoon AST + */ +@SuppressWarnings("unchecked") +public class JDTCommentBuilder { + + private static final Logger LOGGER = Logger.getLogger(JDTCommentBuilder.class); + + private final CompilationUnitDeclaration declarationUnit; + private String filePath; + private CompilationUnit spoonUnit; + private Factory factory; + private ICompilationUnit sourceUnit; + private char[] contents; + + /** + * Creates a JDTCommentBuilder that will insert all comment of the declarationUnit into the Spoon AST + * @param declarationUnit the declaration unit + * @param factory the Spoon AST + */ + 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); + } + + /** + * Start the build process + */ + public void build() { + if (declarationUnit.comments == null) { + return; + } + for (int j = 0; j < declarationUnit.comments.length; j++) { + int[] positions = declarationUnit.comments[j]; + buildComment(positions); + } + } + + /** + * Inserts the comment at the position positions in the AST + * @param positions the position of the comment + */ + private void buildComment(int[] positions) { + int start = positions[0]; + int end = -positions[1]; + + CtComment comment = factory.Core().createComment(); + + // + comment.setCommentType(CtComment.CommentType.BLOCK); + // the inline comments have negative start + if (start < 0) { + comment.setCommentType(CtComment.CommentType.INLINE); + start = -start; + } + // Javadoc comments have negative end position + if (end <= 0) { + comment.setCommentType(CtComment.CommentType.JAVADOC); + end = -end; + } + String commentContent = getCommentContent(start, end); + + int[] lineSeparatorPositions = declarationUnit.compilationResult.lineSeparatorPositions; + SourcePosition sourcePosition = factory.Core().createSourcePosition(spoonUnit, start, start, end, lineSeparatorPositions); + + // create the Spoon comment element + comment.setContent(commentContent); + comment.setPosition(sourcePosition); + + insertCommentInAST(comment); + } + + /** + * 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 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; + } + } + // adds the comment to the nearest element + if (best != null) { + best.addComment(comment); + } + return best; + } + + /** + * Inserts the comment into in AST + * @param comment the comment to insert + */ + private void insertCommentInAST(final CtComment comment) { + CtElement commentParent = findCommentParent(comment); + if (commentParent == null) { + comment.setCommentType(CtComment.CommentType.FILE_COMMENT); + addCommentToNear(comment, new ArrayList(spoonUnit.getDeclaredTypes())); + return; + } + // visitor that inserts the comment in the element + CtInheritanceScanner insertionVisitor = new CtInheritanceScanner() { + private boolean isScanned = false; + @Override + public void scan(CtElement e) { + if (e == null) { + return; + } + // Do not visit the AST, only the first element + if (!isScanned) { + isScanned = true; + if (e.getPosition().getSourceStart() == comment.getPosition().getSourceStart()) { + e.addComment(comment); + return; + } + super.scan(e); + } + } + + @Override + public 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) { + if (comment.getPosition().getLine() <= e.getPosition().getLine()) { + e.addComment(comment); + return; + } + 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 visitCtIf(CtIf e) { + if (!(e.getThenStatement() instanceof CtBlock)) { + if (comment.getPosition().getSourceEnd() <= e.getThenStatement().getPosition().getSourceStart()) { + e.getThenStatement().addComment(comment); + return; + } + } + if (e.getElseStatement() != null) { + if (comment.getPosition().getSourceStart() > e.getThenStatement().getPosition().getSourceEnd() + && comment.getPosition().getSourceEnd() < e.getElseStatement().getPosition().getSourceStart()) { + e.getElseStatement().addComment(comment); + } + } + try { + comment.getParent(); + } catch (ParentNotInitializedException ex) { + e.addComment(comment); + } + } + + @Override + public void scanCtStatement(CtStatement s) { + if (!(s instanceof CtStatementList || s instanceof CtSwitch)) { + s.addComment(comment); + } + } + + @Override + public void visitCtAnonymousExecutable(CtAnonymousExecutable e) { + e.addComment(comment); + } + + @Override + public void visitCtNewArray(CtNewArray e) { + addCommentToNear(comment, new ArrayList(e.getElements())); + try { + comment.getParent(); + } catch (ParentNotInitializedException ex) { + e.addComment(comment); + } + } + + @Override + public void visitCtParameter(CtParameter e) { + e.addComment(comment); + } + }; + insertionVisitor.scan(commentParent); + try { + comment.getParent(); + } catch (ParentNotInitializedException e) { + LOGGER.error(comment + " is not added into the AST", e); + } + } + + /** + * Find the parent of a comment based on the position + * @param comment the comment + * @return the parent of the comment + */ + private CtElement findCommentParent(CtComment comment) { + class FindCommentParentScanner extends CtScanner { + public CtElement commentParent; + + private int start; + private int end; + + FindCommentParentScanner(int start, int end) { + this.start = start; + this.end = end; + } + + @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.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; + } + + /** + * Extract the comment from the content of the class + * @param start the start position of the comment + * @param end the end position of the comment + * @return the content of the comment + */ + private String getCommentContent(int start, int end) { + StringBuilder stringBuilder = new StringBuilder(); + for (int f = start + 2; f < end; f++) { + char content = contents[f]; + stringBuilder.append(content); + } + String doc = stringBuilder.toString(); + return cleanComment(doc); + } + + public static String cleanComment(String comment) { + StringBuffer ret = new StringBuffer(); + String[] lines = comment.split("\n"); + // limit case + if (lines.length == 1) { + return lines[0].replaceAll("^/\\*+", "").replaceAll("\\*+/$", "").trim(); + } + + for (String s : lines) { + String cleanUpLine = s.trim(); + if (cleanUpLine.startsWith("/**")) { + cleanUpLine = cleanUpLine.replaceAll("/\\*+", ""); + } else if (cleanUpLine.endsWith("*/")) { + cleanUpLine = cleanUpLine.replaceAll("\\*+/$", "").replaceAll("^[ \t]*\\*+", ""); + } else { + cleanUpLine = cleanUpLine.replaceAll("^[ \t]*\\*+", ""); + } + ret.append(cleanUpLine); + ret.append("\n"); + } + // clean '\r' + StringBuffer ret2 = new StringBuffer(); + for (int i = 0; i < ret.length(); i++) { + if (ret.charAt(i) != '\r') { + ret2.append(ret.charAt(i)); + } + } + return ret2.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 e3d74063d71..3ab88a61f5f 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilder.java @@ -63,7 +63,6 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression; import org.eclipse.jdt.internal.compiler.ast.IntLiteral; -import org.eclipse.jdt.internal.compiler.ast.Javadoc; import org.eclipse.jdt.internal.compiler.ast.LabeledStatement; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; @@ -152,6 +151,7 @@ import spoon.reflect.code.CtCase; import spoon.reflect.code.CtCatch; import spoon.reflect.code.CtCatchVariable; +import spoon.reflect.code.CtComment; import spoon.reflect.code.CtConditional; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtContinue; @@ -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)) { @@ -1165,37 +1166,6 @@ public List> getBoundedTypesReferences(TypeBinding[] genericT } } - public static String cleanJavadoc(String doc) { - StringBuffer ret = new StringBuffer(); - String[] lines = doc.split("\n"); - - // limit case - if (lines.length == 1) { - return lines[0].replaceAll("^/\\*+", "").replaceAll("\\*+/$", ""); - } - - for (String s : lines) { - String cleanUpLine = s.trim(); - if (cleanUpLine.startsWith("/**")) { - cleanUpLine = cleanUpLine.replaceAll("/\\*+", ""); - } else if (cleanUpLine.endsWith("*/")) { - cleanUpLine = cleanUpLine.replaceAll("\\*+/$", "").replaceAll("^[ \t]*\\*+", ""); - } else { - cleanUpLine = cleanUpLine.replaceAll("^[ \t]*\\*+", ""); - } - ret.append(cleanUpLine); - ret.append("\n"); - } - // clean '\r' - StringBuffer ret2 = new StringBuffer(); - for (int i = 0; i < ret.length(); i++) { - if (ret.charAt(i) != '\r') { - ret2.append(ret.charAt(i)); - } - } - return ret2.toString().trim(); - } - public static Set getModifiers(int mod) { Set ret = EnumSet.noneOf(ModifierKind.class); if ((mod & ClassFileConstants.AccPublic) != 0) { @@ -1442,7 +1412,6 @@ CtType createType(TypeDeclaration typeDeclaration) { // Setting modifiers type.setModifiers(getModifiers(typeDeclaration.modifiers)); - // type.setDocComment(getJavaDoc(typeDeclaration.javadoc)); return type; } @@ -2047,19 +2016,6 @@ public List> getCreatedTypes() { return context.getCreatedTypes(); } - public String getJavaDoc(Javadoc javadoc, CompilationUnitDeclaration declaration) { - if (javadoc != null) { - try { - String s = new String(declaration.compilationResult.compilationUnit.getContents(), javadoc.sourceStart, javadoc.sourceEnd - javadoc.sourceStart + 1); - return cleanJavadoc(s); - } catch (StringIndexOutOfBoundsException e) { - // BCUTAG trouver cause - return null; - } - } - return null; - } - protected CtLocalVariable getLocalVariableDeclaration(final String name) { List reversedElements = new ArrayList(context.stack.size()); for (ASTPair element : context.stack) { @@ -2541,8 +2497,6 @@ public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope s CtConstructor c = factory.Core().createConstructor(); c.setModifiers(getModifiers(constructorDeclaration.modifiers)); - c.setDocComment(getJavaDoc(constructorDeclaration.javadoc, scope.referenceCompilationUnit())); - context.enter(c, constructorDeclaration); if (constructorDeclaration.annotations != null) { @@ -2742,8 +2696,6 @@ public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) { field.setSimpleName(new String(fieldDeclaration.name)); field.setModifiers(getModifiers(fieldDeclaration.modifiers)); - field.setDocComment(getJavaDoc(fieldDeclaration.javadoc, context.compilationunitdeclaration)); - if (fieldDeclaration.annotations != null) { int annotationsLength = fieldDeclaration.annotations.length; for (int i = 0; i < annotationsLength; i++) { @@ -3097,14 +3049,6 @@ public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) { } } - if (scope != null) { - m.setDocComment(getJavaDoc(methodDeclaration.javadoc, scope.referenceCompilationUnit())); - } else if (methodDeclaration.scope != null) { - m.setDocComment(getJavaDoc(methodDeclaration.javadoc, methodDeclaration.scope.referenceCompilationUnit())); - } else { - // null scope for "+methodDeclaration - } - if (methodDeclaration.annotations != null) { for (Annotation a : methodDeclaration.annotations) { a.traverse(this, methodDeclaration.scope); @@ -3769,9 +3713,6 @@ public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) { } else { t = createType(localTypeDeclaration); } - if (localTypeDeclaration.javadoc != null) { - t.setDocComment(getJavaDoc(localTypeDeclaration.javadoc, scope.referenceCompilationUnit())); - } // AST bug HACK (see TypeDeclaration.traverse) if (localTypeDeclaration.fields != null) { @@ -3791,7 +3732,6 @@ public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) { @Override public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) { CtType type = createType(memberTypeDeclaration); - type.setDocComment(getJavaDoc(memberTypeDeclaration.javadoc, scope.referenceCompilationUnit())); return true; } @@ -3799,7 +3739,8 @@ public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) { public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) { if (new String(typeDeclaration.name).equals("package-info")) { CtPackage pack = factory.Package().getOrCreate(new String(typeDeclaration.binding.fPackage.readableName())); - pack.setDocComment(this.getJavaDoc(typeDeclaration.javadoc, scope.referenceContext)); + String s = new String(typeDeclaration.compilationResult.compilationUnit.getContents(), typeDeclaration.javadoc.sourceStart, typeDeclaration.javadoc.sourceEnd - typeDeclaration.javadoc.sourceStart + 1); + pack.addComment(factory.Code().createComment(JDTCommentBuilder.cleanComment(s), CtComment.CommentType.JAVADOC)); context.compilationunitdeclaration = scope.referenceContext; context.enter(pack, typeDeclaration); @@ -3817,8 +3758,6 @@ public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope CtType type = createType(typeDeclaration); pack.addType(type); - type.setDocComment(getJavaDoc(typeDeclaration.javadoc, scope.referenceContext)); - // AST bug HACK if (typeDeclaration.annotations != null) { for (Annotation a : typeDeclaration.annotations) { 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..74542a587a0 --- /dev/null +++ b/src/main/java/spoon/support/reflect/code/CtCommentImpl.java @@ -0,0 +1,87 @@ +/** + * 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 CommentType type; + + @Override + public String getContent() { + return content; + } + + @Override + public E setContent(String content) { + this.content = content; + return (E) this; + } + + @Override + public CommentType getCommentType() { + return type; + } + + @Override + public E setCommentType(CommentType commentType) { + type = commentType; + return (E) this; + } + + @Override + public void accept(CtVisitor visitor) { + visitor.visitCtComment(this); + } + + /** + * The comments are not printed during the CtElement equality. + * The method are thus override for CtComment + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + CtCommentImpl ctComment = (CtCommentImpl) o; + + if (content != null + ? !content.equals(ctComment.content) + : ctComment.content != null) { + return false; + } + return type == ctComment.type; + + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (content != null ? content.hashCode() : 0); + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } +} diff --git a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java index f1a8efc7714..56a2e830c09 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) @@ -159,8 +161,6 @@ public String getSignature() { List> annotations = emptyList(); - String docComment; - SourcePosition position; Map metadata; @@ -189,7 +189,7 @@ private String getDeepRepresentation(CtElement elem) { } @Override - public final boolean equals(Object o) { + public boolean equals(Object o) { boolean ret = false; if ((o instanceof CtElement)) { String current = getDeepRepresentation(this); @@ -227,8 +227,14 @@ public List> getAnnotations() { return Collections.unmodifiableList(annotations); } + @Deprecated public String getDocComment() { - return docComment; + for (CtComment ctComment : comments) { + if (ctComment.getCommentType() == CtComment.CommentType.JAVADOC) { + return ctComment.getContent(); + } + } + return null; } public SourcePosition getPosition() { @@ -239,7 +245,7 @@ public SourcePosition getPosition() { } @Override - public final int hashCode() { + public int hashCode() { HashcodeVisitor pr = new HashcodeVisitor(); pr.scan(this); return pr.getHasCode(); @@ -271,8 +277,15 @@ public boolean removeAnnotation(CtAnnotation annotation) { return (List) annotations != (List) emptyList() && this.annotations.remove(annotation); } + @Deprecated public E setDocComment(String docComment) { - this.docComment = docComment; + for (CtComment ctComment : comments) { + if (ctComment.getCommentType() == CtComment.CommentType.JAVADOC) { + ctComment.setContent(docComment); + return (E) this; + } + } + this.addComment(factory.Code().createComment(docComment, CtComment.CommentType.JAVADOC)); return (E) this; } @@ -496,4 +509,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..51e9e51c07c --- /dev/null +++ b/src/test/java/spoon/test/comment/CommentTest.java @@ -0,0 +1,450 @@ +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.declaration.CtParameter; +import spoon.reflect.factory.Factory; +import spoon.reflect.visitor.filter.TypeFilter; +import spoon.test.comment.testclasses.BlockComment; +import spoon.test.comment.testclasses.InlineComment; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class CommentTest { + + private String newLine = System.getProperty("line.separator"); + + private Factory getSpoonFactory() { + final Launcher launcher = new Launcher(); + launcher.run(new String[] { + "-i", "./src/test/java/spoon/test/comment/testclasses/", + "-o", "./target/spooned/" + }); + launcher.getEnvironment().setGenerateJavadoc(true); + return launcher.getFactory(); + } + + private CtComment createFakeComment(Factory factory, String content) { + return factory.Code().createInlineComment(content); + } + + private CtComment createFakeBlockComment(Factory factory, String content) { + return factory.Code().createComment(content, CtComment.CommentType.BLOCK); + } + + @Test + public void testRemoveComment() { + Factory f = getSpoonFactory(); + CtClass type = (CtClass) f.Type().get(InlineComment.class); + List comments = type.getComments(); + assertEquals(3, comments.size()); + type.removeComment(comments.get(0)); + assertEquals(2, type.getComments().size()); + } + + @Test + public void testInLineComment() { + Factory f = getSpoonFactory(); + CtClass type = (CtClass) f.Type().get(InlineComment.class); + String strType = type.toString(); + + List comments = type.getElements(new TypeFilter(CtComment.class)); + // verify that the number of comment present in the AST is correct + assertEquals(50, comments.size()); + + // verify that all comments present in the AST is printed + for (CtComment comment : comments) { + if (comment.getCommentType() == CtComment.CommentType.FILE_COMMENT) { + // the header of the file is not printed with the toString + continue; + } + assertTrue(comment.toString() + ":" + comment.getParent() + " is not printed", strType.contains(comment.toString())); + } + + assertEquals(3, type.getComments().size()); + assertEquals(CtComment.CommentType.FILE_COMMENT, type.getComments().get(0).getCommentType()); + 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 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 + + "} 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()); + + + 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()); + } + + @Test + public void testBlockComment() { + Factory f = getSpoonFactory(); + CtClass type = (CtClass) f.Type().get(BlockComment.class); + String strType = type.toString(); + + List comments = type.getElements(new TypeFilter(CtComment.class)); + // verify that the number of comment present in the AST is correct + assertEquals(51, comments.size()); + + // verify that all comments present in the AST is printed + for (CtComment comment : comments) { + if (comment.getCommentType() == CtComment.CommentType.FILE_COMMENT) { + // the header of the file is not printed with the toString + continue; + } + assertTrue(comment.toString() + ":" + comment.getParent() + " is not printed", strType.contains(comment.toString())); + } + + assertEquals(4, type.getComments().size()); + assertEquals(createFakeBlockComment(f, "comment class"), type.getComments().get(1)); + + CtField field = type.getField("field"); + assertEquals(2, field.getComments().size()); + assertEquals(createFakeBlockComment(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(createFakeBlockComment(f, "comment static block"), ctAnonymousExecutable.getComments().get(0)); + assertEquals(createFakeBlockComment(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(createFakeBlockComment(f, "comment constructor"), constructor.getComments().get(0)); + // index 0 is the implicit super call + assertEquals(createFakeBlockComment(f, "Comment in constructor"), constructor.getBody().getStatement(1)); + assertEquals("/* comment constructor */" + newLine + + "public BlockComment() {" + newLine + + " /* Comment in constructor */" + newLine + + "}", constructor.toString()); + + CtMethod m = type.getMethod("m"); + assertEquals(1, m.getComments().size()); + assertEquals(createFakeBlockComment(f, "comment method"), m.getComments().get(0)); + assertEquals(createFakeBlockComment(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(createFakeBlockComment(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(createFakeBlockComment(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(createFakeBlockComment(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(createFakeBlockComment(f, "comment constructor call"), ctConstructorCall.getComments().get(0)); + assertEquals("/* comment constructor call */" + newLine + + "new spoon.test.comment.testclasses.BlockComment()", ctConstructorCall.toString()); + + CtInvocation ctInvocation = m1.getBody().getStatement(4); + assertEquals(createFakeBlockComment(f, "comment invocation"), ctInvocation.getComments().get(0)); + assertEquals("/* comment invocation */" + newLine + + "spoon.test.comment.testclasses.BlockComment.this.m()", ctInvocation.toString()); + + CtLocalVariable ctLocalVariable = m1.getBody().getStatement(5); + assertEquals(createFakeBlockComment(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(createFakeBlockComment(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(createFakeBlockComment(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(createFakeBlockComment(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(createFakeBlockComment(f, "comment synchronized"), ctSynchronized.getComments().get(0)); + assertEquals("/* comment synchronized */" + newLine + + "synchronized(spoon.test.comment.testclasses.BlockComment.this) {" + newLine + + " /* comment in synchronized */" + newLine + + "}", ctSynchronized.toString()); + + CtReturn ctReturn = m1.getBody().getStatement(10); + assertEquals(createFakeBlockComment(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()); + } + + @Test + public void testInsertNewComment () { + Factory f = getSpoonFactory(); + CtClass type = (CtClass) f.Type().get(InlineComment.class); + + CtMethod method = f.Core().createMethod(); + method.setSimpleName("newMethod"); + method.setBody(f.Core().createBlock()); + method.setType(f.Type().VOID_PRIMITIVE); + + type.addMethod(method); + + method.addComment(createFakeComment(f, "comment method")); + method.getBody().addStatement(createFakeComment(f, "comment empty block")); + + assertEquals("// comment method" + newLine + + "void newMethod() {" + newLine + + " // comment empty block" + newLine + + "}", method.toString()); + + method.getBody().removeStatement(method.getBody().getStatement(0)); + + CtLocalVariable i = f.Code().createLocalVariable(f.Type().INTEGER_PRIMITIVE, "i", null); + i.addComment(createFakeComment(f, "comment local variable")); + method.getBody().addStatement(i); + + assertEquals("// comment method" + newLine + + "void newMethod() {" + newLine + + " // comment local variable" + newLine + + " int i;" + newLine + + "}", method.toString()); + } + + @Test + public void testCoreFactory() { + Factory spoonFactory = getSpoonFactory(); + + CtComment comment = spoonFactory.Core().createComment(); + assertEquals("/* */", comment.toString()); + + comment.setContent("comment"); + assertEquals("/* comment */", comment.toString()); + + comment.setCommentType(CtComment.CommentType.INLINE); + assertEquals(CtComment.CommentType.INLINE, comment.getCommentType()); + assertEquals("// comment", comment.toString()); + + comment.setCommentType(CtComment.CommentType.BLOCK); + assertEquals(CtComment.CommentType.BLOCK, comment.getCommentType()); + } + + @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()); + } +} diff --git a/src/test/java/spoon/test/comment/testclasses/BlockComment.java b/src/test/java/spoon/test/comment/testclasses/BlockComment.java new file mode 100644 index 00000000000..f2db766ad4c --- /dev/null +++ b/src/test/java/spoon/test/comment/testclasses/BlockComment.java @@ -0,0 +1,102 @@ +/* + * Top File + */ +package spoon.test.comment.testclasses; + +/* comment class */ +/** + * JavaDoc class + */ +public class BlockComment { + /* Comment Field */ + private int field /* comment in field */ = 10; + + /* comment static block */ + static { + /* comment inside static */ + } + + /* comment constructor */ + public BlockComment() { + /* 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 BlockComment(); + + /* 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; + } + + public /* comment before type */ void /* comment before name */ m2 /* comment before parameters */ (/* comment before type parameter */ int /* comment before name parameter */ i /* comment after parameter */ ) /* comment before throws */ throws /* comment before exception 1 */ Exception, /* comment before exception 2 */ Error /* comment before block */ { + + } + + public void m3() { + if (true){ + /* comment empty if */ + } + /* comment before else */ + else { + /* comment empty else */ + } + /* comment if without block */ + if (true) + /* comment then if without block */ + m3(); + /* comment else without block */ + else /* comment else if without block */ + m3(); + } + /* comment after class */ +} \ No newline at end of file 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..ef0e9fca029 --- /dev/null +++ b/src/test/java/spoon/test/comment/testclasses/InlineComment.java @@ -0,0 +1,117 @@ +/* + * Top File + * Line 2 + */ +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; + } + + public // comment before type + void // comment before name + m2 // comment before parameters + (// comment before type parameter + int // comment before name parameter + i + // comment after parameter + ) + // comment before throws + throws + // comment before exception 1 + Exception, + // comment before exception 2 + Error + // comment before block + {} + + public void m3() { + if (true){ + // comment empty if + } + // comment before else + else { + // comment empty else + } + // comment if without block + if (true) + // comment then if without block + m3(); + // comment else without block + else + // comment else if without block + m3(); + } + // comment after class +} \ No newline at end of file diff --git a/src/test/java/spoon/test/javadoc/JavaDocTest.java b/src/test/java/spoon/test/javadoc/JavaDocTest.java index 31c3535cffb..e87a6cfc983 100644 --- a/src/test/java/spoon/test/javadoc/JavaDocTest.java +++ b/src/test/java/spoon/test/javadoc/JavaDocTest.java @@ -23,7 +23,7 @@ public void testJavaDocReprint() throws Exception { CtClass aClass = factory.Class().get(Bar.class); Assert.assertEquals("public class Bar {" + System.lineSeparator() - + " /** " + System.lineSeparator() + + " /**" + System.lineSeparator() + " * Creates an annotation type." + System.lineSeparator() + " * " + System.lineSeparator() + " * @param owner" + System.lineSeparator() diff --git a/src/test/java/spoon/test/prettyprinter/LinesTest.java b/src/test/java/spoon/test/prettyprinter/LinesTest.java index 5c231a2ca33..cc8932ea157 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;