diff options
author | Roel Spilker <r.spilker@gmail.com> | 2009-11-27 04:18:28 +0100 |
---|---|---|
committer | Roel Spilker <r.spilker@gmail.com> | 2009-11-27 04:23:41 +0100 |
commit | 391db3dcecdd0d94eb76b656e655346891b02bb4 (patch) | |
tree | 592d79167f4f5a6ce92813cfd962fa51228d3ee0 | |
parent | e54c3f36e3122dfe34a119178cfcca2dcdbad998 (diff) | |
download | lombok-391db3dcecdd0d94eb76b656e655346891b02bb4.tar.gz lombok-391db3dcecdd0d94eb76b656e655346891b02bb4.tar.bz2 lombok-391db3dcecdd0d94eb76b656e655346891b02bb4.zip |
Thorough work on inserting comments in the proper place for delombok; should now work fine with GWT native methods!
NON-NLS-1 is still theoretically problematic, but that'll be a fix for another day.
Also added ability to recognize that nothing has changed, which will copy the original file instead of reparsing it.
19 files changed, 169 insertions, 35 deletions
@@ -14,7 +14,7 @@ <classpathentry kind="lib" path="deps/lombok/org.eclipse.equinox.common_3.5.0.v20090520-1800.jar"/> <classpathentry kind="lib" path="deps/junit-4.7.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/lombok.patcher"/> - <classpathentry kind="lib" path="deps/lombok/tools.jar"/> + <classpathentry kind="lib" path="deps/lombok/tools.jar" sourcepath="C:/javasource/javasources.zip"/> <classpathentry kind="lib" path="deps/lombok/spi-0.2.4.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/src/core/lombok/javac/JavacTransformer.java b/src/core/lombok/javac/JavacTransformer.java index d004414d..5f145460 100644 --- a/src/core/lombok/javac/JavacTransformer.java +++ b/src/core/lombok/javac/JavacTransformer.java @@ -42,7 +42,7 @@ public class JavacTransformer { this.handlers = HandlerLibrary.load(messager); } - public void transform(Context context, Iterable<JCCompilationUnit> compilationUnits) { + public boolean transform(Context context, Iterable<JCCompilationUnit> compilationUnits) { List<JavacAST> asts = new ArrayList<JavacAST>(); for (JCCompilationUnit unit : compilationUnits) asts.add(new JavacAST(messager, context, unit)); @@ -57,6 +57,11 @@ public class JavacTransformer { for (JavacAST ast : asts) { ast.traverse(new AnnotationVisitor()); } + + for (JavacAST ast : asts) { + if (ast.isChanged()) return true; + } + return false; } private class AnnotationVisitor extends JavacASTAdapter { diff --git a/src/core/lombok/javac/handlers/HandleCleanup.java b/src/core/lombok/javac/handlers/HandleCleanup.java index 88a8e1d7..2c89d9ad 100644 --- a/src/core/lombok/javac/handlers/HandleCleanup.java +++ b/src/core/lombok/javac/handlers/HandleCleanup.java @@ -21,6 +21,7 @@ */ package lombok.javac.handlers; +import static lombok.javac.handlers.JavacHandlerUtil.markAnnotationAsProcessed; import lombok.Cleanup; import lombok.core.AnnotationValues; import lombok.core.AST.Kind; @@ -53,6 +54,7 @@ import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) public class HandleCleanup implements JavacAnnotationHandler<Cleanup> { @Override public boolean handle(AnnotationValues<Cleanup> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, Cleanup.class); String cleanupName = annotation.getInstance().value(); if (cleanupName.length() == 0) { annotationNode.addError("cleanupName cannot be the empty string."); diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java index eef7f78d..a0315d08 100644 --- a/src/core/lombok/javac/handlers/HandleData.java +++ b/src/core/lombok/javac/handlers/HandleData.java @@ -59,6 +59,7 @@ import com.sun.tools.javac.util.List; @ProviderFor(JavacAnnotationHandler.class) public class HandleData implements JavacAnnotationHandler<Data> { @Override public boolean handle(AnnotationValues<Data> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, Data.class); JavacNode typeNode = annotationNode.up(); JCClassDecl typeDecl = null; if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl)typeNode.get(); diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index 61a4ef63..f388336d 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -72,6 +72,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd } @Override public boolean handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, EqualsAndHashCode.class); EqualsAndHashCode ann = annotation.getInstance(); List<String> excludes = List.from(ann.exclude()); List<String> includes = List.from(ann.of()); diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index e60e426d..b0343eaf 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -80,8 +80,10 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { } @Override public boolean handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, Getter.class); JavacNode fieldNode = annotationNode.up(); AccessLevel level = annotation.getInstance().value(); + if (level == AccessLevel.NONE) return true; return createGetterForField(level, fieldNode, annotationNode, true); diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 84032e9c..1d029f33 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -82,8 +82,10 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { } @Override public boolean handle(AnnotationValues<Setter> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, Setter.class); JavacNode fieldNode = annotationNode.up(); AccessLevel level = annotation.getInstance().value(); + if (level == AccessLevel.NONE) return true; return createSetterForField(level, fieldNode, annotationNode, true); diff --git a/src/core/lombok/javac/handlers/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java index e7879dd1..8a185e87 100644 --- a/src/core/lombok/javac/handlers/HandleSneakyThrows.java +++ b/src/core/lombok/javac/handlers/HandleSneakyThrows.java @@ -22,6 +22,7 @@ package lombok.javac.handlers; import static lombok.javac.handlers.JavacHandlerUtil.chainDots; +import static lombok.javac.handlers.JavacHandlerUtil.markAnnotationAsProcessed; import java.util.ArrayList; import java.util.Collection; @@ -49,6 +50,7 @@ import com.sun.tools.javac.util.List; @ProviderFor(JavacAnnotationHandler.class) public class HandleSneakyThrows implements JavacAnnotationHandler<SneakyThrows> { @Override public boolean handle(AnnotationValues<SneakyThrows> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, SneakyThrows.class); Collection<String> exceptionNames = annotation.getRawExpressions("value"); List<JCExpression> memberValuePairs = ast.getArguments(); diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java index c86d99c6..eaa0ad6d 100644 --- a/src/core/lombok/javac/handlers/HandleSynchronized.java +++ b/src/core/lombok/javac/handlers/HandleSynchronized.java @@ -51,10 +51,12 @@ public class HandleSynchronized implements JavacAnnotationHandler<Synchronized> private static final String STATIC_LOCK_NAME = "$LOCK"; @Override public boolean handle(AnnotationValues<Synchronized> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, Synchronized.class); JavacNode methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != Kind.METHOD || !(methodNode.get() instanceof JCMethodDecl)) { annotationNode.addError("@Synchronized is legal only on methods."); + return true; } @@ -62,6 +64,7 @@ public class HandleSynchronized implements JavacAnnotationHandler<Synchronized> if ((method.mods.flags & Flags.ABSTRACT) != 0) { annotationNode.addError("@Synchronized is legal only on concrete methods."); + return true; } boolean isStatic = (method.mods.flags & Flags.STATIC) != 0; diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index f7251ab8..dd3df620 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -68,6 +68,8 @@ public class HandleToString implements JavacAnnotationHandler<ToString> { } @Override public boolean handle(AnnotationValues<ToString> annotation, JCAnnotation ast, JavacNode annotationNode) { + markAnnotationAsProcessed(annotationNode, ToString.class); + ToString ann = annotation.getInstance(); List<String> excludes = List.from(ann.exclude()); List<String> includes = List.from(ann.of()); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 34d8b849..caa8a998 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -21,6 +21,7 @@ */ package lombok.javac.handlers; +import java.lang.annotation.Annotation; import java.util.regex.Pattern; import lombok.AccessLevel; @@ -34,7 +35,9 @@ import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; @@ -58,6 +61,54 @@ public class JavacHandlerUtil { } /** + * Removes the annotation from javac's AST, then removes it from lombok's AST, + * then removes any import statement that imports this exact annotation (not star imports). + */ + public static void markAnnotationAsProcessed(JavacNode annotation, Class<? extends Annotation> annotationType) { + JavacNode parentNode = annotation.directUp(); + switch (parentNode.getKind()) { + case FIELD: + case ARGUMENT: + case LOCAL: + JCVariableDecl variable = (JCVariableDecl) parentNode.get(); + variable.mods.annotations = filterList(variable.mods.annotations, annotation.get()); + break; + case METHOD: + JCMethodDecl method = (JCMethodDecl) parentNode.get(); + method.mods.annotations = filterList(method.mods.annotations, annotation.get()); + break; + default: + throw new IllegalStateException("Don't know how to remove annotations from: " + parentNode.getKind()); + } + parentNode.removeChild(annotation); + + JCCompilationUnit unit = (JCCompilationUnit) annotation.top().get(); + deleteImportFromCompilationUnit(unit, annotationType.getName()); + } + + private static void deleteImportFromCompilationUnit(JCCompilationUnit unit, String name) { + List<JCTree> newDefs = List.nil(); + + for (JCTree def : unit.defs) { + boolean delete = false; + if (def instanceof JCImport) { + JCImport imp0rt = (JCImport)def; + delete = (!imp0rt.staticImport && imp0rt.qualid.toString().equals(name)); + } + if (!delete) newDefs = newDefs.append(def); + } + unit.defs = newDefs; + } + + private static List<JCAnnotation> filterList(List<JCAnnotation> annotations, JCTree jcTree) { + List<JCAnnotation> newAnnotations = List.nil(); + for (JCAnnotation ann : annotations) { + if (jcTree != ann) newAnnotations = newAnnotations.append(ann); + } + return newAnnotations; + } + + /** * Translates the given field into all possible getter names. * Convenient wrapper around {@link TransformationsUtil#toAllGetterNames(CharSequence, boolean)}. */ diff --git a/src/delombok/lombok/delombok/Comment.java b/src/delombok/lombok/delombok/Comment.java index c264733f..55a5c46d 100644 --- a/src/delombok/lombok/delombok/Comment.java +++ b/src/delombok/lombok/delombok/Comment.java @@ -23,11 +23,21 @@ package lombok.delombok; public final class Comment { final int pos; + final int prevEndPos; final String content; + final int endPos; + final boolean newLine; - public Comment(int pos, String content) { + public Comment(int prevEndPos, int pos, int endPos, String content, boolean newLine) { this.pos = pos; + this.prevEndPos = prevEndPos; + this.endPos = endPos; this.content = content; + this.newLine = newLine; + } + + public boolean isConnected() { + return !newLine && prevEndPos == pos; } public String summary() { diff --git a/src/delombok/lombok/delombok/CommentCollectingScanner.java b/src/delombok/lombok/delombok/CommentCollectingScanner.java index 6f593c7f..c38aef24 100644 --- a/src/delombok/lombok/delombok/CommentCollectingScanner.java +++ b/src/delombok/lombok/delombok/CommentCollectingScanner.java @@ -80,6 +80,20 @@ public class CommentCollectingScanner extends Scanner { @Override protected void processComment(CommentStyle style) { - comments.add(pos(), new String(getRawCharacters(pos(), endPos()))); + int prevEndPos = prevEndPos(); + int pos = pos(); + boolean newLine = containsNewLine(prevEndPos, pos); + String content = new String(getRawCharacters(pos, endPos())); + comments.add(prevEndPos, pos, endPos(), content, newLine); + } + + + private boolean containsNewLine(int from, int to) { + for (char c : getRawCharacters(from, to)) { + if (c == '\n' || c == '\r') { + return true; + } + } + return false; } }
\ No newline at end of file diff --git a/src/delombok/lombok/delombok/CommentPreservingParser.java b/src/delombok/lombok/delombok/CommentPreservingParser.java index 51df5ed1..9fd8778c 100644 --- a/src/delombok/lombok/delombok/CommentPreservingParser.java +++ b/src/delombok/lombok/delombok/CommentPreservingParser.java @@ -24,11 +24,13 @@ package lombok.delombok; import java.io.IOException; import java.io.Writer; import java.util.Collections; +import java.util.Date; import javax.annotation.processing.Messager; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; +import javax.tools.JavaFileObject; import javax.tools.Diagnostic.Kind; import lombok.javac.JavacTransformer; @@ -52,7 +54,7 @@ public class CommentPreservingParser { this.encoding = encoding; } - public ParseResult parseFile(String fileName) throws IOException { + public ParseResult parse(String fileName, boolean forceProcessing) throws IOException { Context context = new Context(); Options.instance(context).put(OptionName.ENCODING, encoding); @@ -74,9 +76,8 @@ public class CommentPreservingParser { @SuppressWarnings("deprecation") JCCompilationUnit cu = compiler.parse(fileName); - new JavacTransformer(messager).transform(context, Collections.singleton(cu)); - - return new ParseResult(comments.comments, cu); + boolean changed = new JavacTransformer(messager).transform(context, Collections.singleton(cu)); + return new ParseResult(comments.comments, cu, forceProcessing || changed); } private static final Messager messager = new Messager() { @@ -100,21 +101,33 @@ public class CommentPreservingParser { static class Comments { List<Comment> comments = List.nil(); - void add(int pos, String content) { - comments = comments.append(new Comment(pos, content)); + void add(int prevEndPos, int pos, int endPos, String content, boolean newLine) { + comments = comments.append(new Comment(prevEndPos, pos, endPos, content, newLine)); } } public static class ParseResult { private final List<Comment> comments; private final JCCompilationUnit compilationUnit; + private final boolean changed; - private ParseResult(List<Comment> comments, JCCompilationUnit compilationUnit) { + private ParseResult(List<Comment> comments, JCCompilationUnit compilationUnit, boolean changed) { this.comments = comments; this.compilationUnit = compilationUnit; + this.changed = changed; } - public void print(Writer out) { + public void print(Writer out) throws IOException { + if (!changed) { + JavaFileObject sourceFile = compilationUnit.getSourceFile(); + if (sourceFile != null) { + out.write(sourceFile.getCharContent(true).toString()); + System.out.println(out.toString()); + return; + } + } + + out.write("// Generated by delombok at " + new Date() + "\n"); compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments)); } } diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java index 2fb62e53..611dd417 100644 --- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java +++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java @@ -179,7 +179,32 @@ public class PrettyCommentsPrinter extends JCTree.Visitor { align(); } } - + + private void consumeTrailingComments(int from) throws IOException { + boolean shouldIndent = !newLine; + Comment head = comments.head; + while (comments.nonEmpty() && head.prevEndPos == from) { + from = head.endPos; + if (!head.isConnected()) { + if (head.newLine) { + if (!newLine) { + println(); + align(); + } + } + else { + print(" "); + } + } + print(head.content); + comments = comments.tail; + head = comments.head; + } + if (newLine && shouldIndent) { + align(); + } + } + /** The output stream on which trees are printed. */ Writer out; @@ -277,15 +302,17 @@ public class PrettyCommentsPrinter extends JCTree.Visitor { if (tree == null) print("/*missing*/"); else { consumeComments(tree.pos); - tree.accept(this); - consumeComments(endPos(tree)); + tree.accept(this); + int endPos = endPos(tree); + consumeTrailingComments(endPos); + consumeComments(endPos); } } catch (UncheckedIOException ex) { IOException e = new IOException(ex.getMessage()); e.initCause(ex); throw e; } finally { - this.prec = prevPrec; + this.prec = prevPrec; } } @@ -301,7 +328,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor { public void printStat(JCTree tree) throws IOException { printExpr(tree, TreeInfo.notExpression); } - + /** Derived visitor method: print list of expression trees, separated by given string. * @param sep the separator string */ diff --git a/test/delombok/src/lombok/delombok/TestLombokFiles.java b/test/delombok/src/lombok/delombok/TestLombokFiles.java index 10782830..f04a0a34 100644 --- a/test/delombok/src/lombok/delombok/TestLombokFiles.java +++ b/test/delombok/src/lombok/delombok/TestLombokFiles.java @@ -52,10 +52,9 @@ public class TestLombokFiles { public void testSources() throws Exception { File[] listFiles = BEFORE_FOLDER.listFiles(); for (File file : listFiles) { - ParseResult parseResult = parser.parseFile(file.toString()); + ParseResult parseResult = parser.parse(file.toString(), true); StringWriter writer = new StringWriter(); parseResult.print(writer); - System.out.println(writer); compare(file.getName(), readAfter(file), writer.toString()); } } @@ -63,6 +62,9 @@ public class TestLombokFiles { private void compare(String name, String expectedFile, String actualFile) { String[] expectedLines = expectedFile.split("(\\r?\\n)"); String[] actualLines = actualFile.split("(\\r?\\n)"); + if (actualLines[0].startsWith("// Generated by delombok at ")) { + actualLines[0] = ""; + } expectedLines = removeBlanks(expectedLines); actualLines = removeBlanks(actualLines); int size = Math.min(expectedLines.length, actualLines.length); diff --git a/test/delombok/src/lombok/delombok/TestSourceFiles.java b/test/delombok/src/lombok/delombok/TestSourceFiles.java index b662651d..91ace773 100644 --- a/test/delombok/src/lombok/delombok/TestSourceFiles.java +++ b/test/delombok/src/lombok/delombok/TestSourceFiles.java @@ -53,7 +53,7 @@ public class TestSourceFiles { public void testSources() throws Exception { File[] listFiles = BEFORE_FOLDER.listFiles(); for (File file : listFiles) { - ParseResult parseResult = parser.parseFile(file.toString()); + ParseResult parseResult = parser.parse(file.toString(), true); StringWriter writer = new StringWriter(); parseResult.print(writer); compare(file.getName(), readAfter(file), writer.toString()); @@ -71,6 +71,9 @@ public class TestSourceFiles { private void compare(String name, String expectedFile, String actualFile) { String[] expectedLines = expectedFile.split("(\\r?\\n)"); String[] actualLines = actualFile.split("(\\r?\\n)"); + if (actualLines[0].startsWith("// Generated by delombok at ")) { + actualLines[0] = ""; + } expectedLines = removeBlanks(expectedLines); actualLines = removeBlanks(actualLines); int size = Math.min(expectedLines.length, actualLines.length); @@ -78,7 +81,7 @@ public class TestSourceFiles { String expected = expectedLines[i]; String actual = actualLines[i]; if (!expected.equals(actual)) { - fail(String.format("Difference in line %s(%d):\n`%s`\n`%s`\n", name, i, expected, actual)); + fail(String.format("Difference in line %s(%d):\nExpected `%s`\nGot `%s`\n", name, i, expected, actual)); } } if (expectedLines.length > actualLines.length) { diff --git a/test/lombok/resource/after/CommentsInterspersed.java b/test/lombok/resource/after/CommentsInterspersed.java index ec5374c0..f3841606 100644 --- a/test/lombok/resource/after/CommentsInterspersed.java +++ b/test/lombok/resource/after/CommentsInterspersed.java @@ -1,26 +1,20 @@ -import lombok.Getter; - +/* cmt */ +/* cmt2 */ +/* cmt3 */ /*bla */ public class CommentsInterspersed { /** javadoc for field */ private int x; /* bla2 */ - @Getter() - private String test = "foo"; - //$NON-NLS-1$ + private String test = "foo"; //$NON-NLS-1$ /** Javadoc on method */ - public native void gwtTest(); - /*-{ + public native void gwtTest(); /*-{ javascript; }-*/ - public CommentsInterspersed() { - } - public String getTest() { - return this.test; + return test; } -} -//haha! +} //haha! diff --git a/test/lombok/resource/before/CommentsInterspersed.java b/test/lombok/resource/before/CommentsInterspersed.java index 2a32d309..455c680d 100644 --- a/test/lombok/resource/before/CommentsInterspersed.java +++ b/test/lombok/resource/before/CommentsInterspersed.java @@ -1,4 +1,4 @@ -import lombok.Getter; +import /* cmt */ lombok./* cmt2 */Getter /* cmt3 */ ; public /*bla */ class CommentsInterspersed { /** javadoc for field */ |