diff options
| author | Reinier Zwitserloot <reinier@tipit.to> | 2009-11-27 09:53:08 +0100 |
|---|---|---|
| committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-11-27 09:53:08 +0100 |
| commit | 9678c58ec86a18605e62ff813005fddb1cefb392 (patch) | |
| tree | 1aec17de05371b3a5ca02e25793e43e33c70e9b3 /src/delombok | |
| parent | 6838c6be2f8bd8935fa6853067183cf11c013fcd (diff) | |
| parent | 2b61c2534b22f07a13d8cb4c97c2ad323c6c4597 (diff) | |
| download | lombok-9678c58ec86a18605e62ff813005fddb1cefb392.tar.gz lombok-9678c58ec86a18605e62ff813005fddb1cefb392.tar.bz2 lombok-9678c58ec86a18605e62ff813005fddb1cefb392.zip | |
Merge branch 'delombok'
Diffstat (limited to 'src/delombok')
| -rw-r--r-- | src/delombok/lombok/delombok/Comment.java | 46 | ||||
| -rw-r--r-- | src/delombok/lombok/delombok/CommentCollectingScanner.java | 95 | ||||
| -rw-r--r-- | src/delombok/lombok/delombok/CommentPreservingParser.java | 137 | ||||
| -rw-r--r-- | src/delombok/lombok/delombok/Delombok.java | 273 | ||||
| -rw-r--r-- | src/delombok/lombok/delombok/PrettyCommentsPrinter.java | 1377 |
5 files changed, 1928 insertions, 0 deletions
diff --git a/src/delombok/lombok/delombok/Comment.java b/src/delombok/lombok/delombok/Comment.java new file mode 100644 index 00000000..55a5c46d --- /dev/null +++ b/src/delombok/lombok/delombok/Comment.java @@ -0,0 +1,46 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +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 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() { + return String.format("%d: %s", pos, content); + } +} diff --git a/src/delombok/lombok/delombok/CommentCollectingScanner.java b/src/delombok/lombok/delombok/CommentCollectingScanner.java new file mode 100644 index 00000000..361d975c --- /dev/null +++ b/src/delombok/lombok/delombok/CommentCollectingScanner.java @@ -0,0 +1,95 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.delombok; + +import java.nio.CharBuffer; + +import lombok.delombok.CommentPreservingParser.Comments; + +import com.sun.tools.javac.parser.Scanner; +import com.sun.tools.javac.util.Context; + +public class CommentCollectingScanner extends Scanner { + private final Comments comments; + + /** A factory for creating scanners. */ + public static class Factory extends Scanner.Factory { + private final Context context; + + public static void preRegister(final Context context) { + context.put(scannerFactoryKey, new Context.Factory<Scanner.Factory>() { + public CommentCollectingScanner.Factory make() { + return new Factory(context); + } + }); + } + + /** Create a new scanner factory. */ + protected Factory(Context context) { + super(context); + this.context = context; + } + + @Override + public Scanner newScanner(CharSequence input) { + if (input instanceof CharBuffer) { + return new CommentCollectingScanner(this, (CharBuffer)input, context.get(Comments.class)); + } + char[] array = input.toString().toCharArray(); + return newScanner(array, array.length); + } + + @Override + public Scanner newScanner(char[] input, int inputLength) { + return new CommentCollectingScanner(this, input, inputLength, context.get(Comments.class)); + } + } + + + public CommentCollectingScanner(CommentCollectingScanner.Factory factory, CharBuffer charBuffer, Comments comments) { + super(factory, charBuffer); + this.comments = comments; + } + + public CommentCollectingScanner(CommentCollectingScanner.Factory factory, char[] input, int inputLength, Comments comments) { + super(factory, input, inputLength); + this.comments = comments; + } + + @Override + protected void processComment(CommentStyle style) { + 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; + } +} diff --git a/src/delombok/lombok/delombok/CommentPreservingParser.java b/src/delombok/lombok/delombok/CommentPreservingParser.java new file mode 100644 index 00000000..c776a224 --- /dev/null +++ b/src/delombok/lombok/delombok/CommentPreservingParser.java @@ -0,0 +1,137 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +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; + +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.main.OptionName; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Options; + +public class CommentPreservingParser { + private final String encoding; + + public CommentPreservingParser() { + this("utf-8"); + } + + public CommentPreservingParser(String encoding) { + this.encoding = encoding; + } + + public ParseResult parse(String fileName, boolean forceProcessing) throws IOException { + Context context = new Context(); + + Options.instance(context).put(OptionName.ENCODING, encoding); + + CommentCollectingScanner.Factory.preRegister(context); + + JavaCompiler compiler = new JavaCompiler(context) { + @Override + protected boolean keepComments() { + return true; + } + }; + compiler.genEndPos = true; + + Comments comments = new Comments(); + context.put(Comments.class, comments); + + comments.comments = List.nil(); + + @SuppressWarnings("deprecation") + JCCompilationUnit cu = compiler.parse(fileName); + + 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() { + @Override public void printMessage(Kind kind, CharSequence msg) { + System.out.printf("%s: %s\n", kind, msg); + } + + @Override public void printMessage(Kind kind, CharSequence msg, Element e) { + System.out.printf("%s: %s\n", kind, msg); + } + + @Override public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a) { + System.out.printf("%s: %s\n", kind, msg); + } + + @Override public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) { + System.out.printf("%s: %s\n", kind, msg); + } + }; + + static class Comments { + List<Comment> comments = List.nil(); + + 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, boolean changed) { + this.comments = comments; + this.compilationUnit = compilationUnit; + this.changed = changed; + } + + public void print(Writer out) throws IOException { + if (!changed) { + JavaFileObject sourceFile = compilationUnit.getSourceFile(); + if (sourceFile != null) { + out.write(sourceFile.getCharContent(true).toString()); + return; + } + } + + out.write("// Generated by delombok at " + new Date() + "\n"); + compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments)); + } + + public boolean isChanged() { + return changed; + } + } +} diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java new file mode 100644 index 00000000..e05f4982 --- /dev/null +++ b/src/delombok/lombok/delombok/Delombok.java @@ -0,0 +1,273 @@ +package lombok.delombok; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.List; + +import lombok.delombok.CommentPreservingParser.ParseResult; + +import com.zwitserloot.cmdreader.CmdReader; +import com.zwitserloot.cmdreader.Description; +import com.zwitserloot.cmdreader.Excludes; +import com.zwitserloot.cmdreader.InvalidCommandLineException; +import com.zwitserloot.cmdreader.Mandatory; +import com.zwitserloot.cmdreader.Parameterized; +import com.zwitserloot.cmdreader.Sequential; +import com.zwitserloot.cmdreader.Shorthand; + +public class Delombok { + private Charset charset = Charset.defaultCharset(); + private CommentPreservingParser parser = new CommentPreservingParser(); + private PrintStream feedback = System.err; + private boolean verbose; + private boolean force = false; + + /** If null, output to standard out. */ + private File output = null; + + private static class CmdArgs { + @Shorthand("v") + @Description("Print the name of each file as it is being delombok-ed.") + @Excludes("quiet") + private boolean verbose; + + @Shorthand("q") + @Description("No warnings or errors will be emitted to standard error") + @Excludes("verbose") + private boolean quiet; + + @Shorthand("e") + @Description("Sets the encoding of your source files. Defaults to the system default charset. Example: \"UTF-8\"") + @Parameterized + private String encoding; + + @Shorthand("p") + @Description("Print delombok-ed code to standard output instead of saving it in target directory") + private boolean print; + + @Shorthand("d") + @Description("Directory to save delomboked files to") + @Mandatory(onlyIfNot="print") + @Parameterized + private String target; + + @Description("Files to delombok. Provide either a file, or a directory. If you use a directory, all files in it (recursive) are delombok-ed") + @Sequential + @Parameterized + private List<String> input = new ArrayList<String>(); + + private boolean help; + } + + public static void main(String[] rawArgs) { + CmdReader<CmdArgs> reader = CmdReader.of(CmdArgs.class); + CmdArgs args; + try { + args = reader.make(rawArgs); + } catch (InvalidCommandLineException e) { + System.err.println("ERROR: " + e.getMessage()); + System.err.println(reader.generateCommandLineHelp("delombok")); + System.exit(1); + return; + } + + if (args.help || args.input.isEmpty()) { + if (args.input.isEmpty()) System.err.println("ERROR: no files or directories to delombok specified."); + System.err.println(reader.generateCommandLineHelp("delombok")); + System.exit(args.input.isEmpty() ? 1 : 0); + return; + } + + Delombok delombok = new Delombok(); + + if (args.quiet) delombok.setFeedback(new PrintStream(new OutputStream() { + @Override public void write(int b) throws IOException { + //dummy - do nothing. + } + })); + + if (args.encoding != null) { + try { + delombok.setCharset(args.encoding); + } catch (UnsupportedCharsetException e) { + System.err.println("ERROR: Not a known charset: " + args.encoding); + System.exit(1); + return; + } + } + + if (args.verbose) delombok.setVerbose(true); + if (args.print) delombok.setOutputToStandardOut(); + else delombok.setOutput(new File(args.target)); + + for (String in : args.input) { + try { + File f = new File(in); + if (f.isFile()) { + delombok.delombok(f.getParentFile(), f.getName()); + } else if (f.isDirectory()) { + delombok.delombok(f); + } else if (!f.exists()) { + if (!args.quiet) System.err.println("WARNING: does not exist - skipping: " + f); + } else { + if (!args.quiet) System.err.println("WARNING: not a standard file or directory - skipping: " + f); + } + } catch (Exception e) { + if (!args.quiet) { + String msg = e.getMessage(); + if (msg != null && msg.startsWith("DELOMBOK: ")) System.err.println(msg.substring("DELOMBOK: ".length())); + else { + e.printStackTrace(); + } + System.exit(1); + return; + } + } + } + } + + public void setCharset(String charsetName) throws UnsupportedCharsetException { + charset = Charset.forName(charsetName); + } + + public void setForceProcess(boolean force) { + this.force = force; + } + + public void setFeedback(PrintStream feedback) { + this.feedback = feedback; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public void setOutput(File dir) { + if (dir.isFile() || (!dir.isDirectory() && dir.getName().endsWith(".java"))) throw new IllegalArgumentException( + "DELOMBOK: delombok will only write to a directory. " + + "If you want to delombok a single file, use -p to output to standard output, then redirect this to a file:\n" + + "delombok MyJavaFile.java -p >MyJavaFileDelombok.java"); + output = dir; + } + + public void setOutputToStandardOut() { + this.output = null; + } + + public void delombok(File base) throws IOException { + delombok0(base, "", 0); + } + + private void delombok0(File base, String suffix, int loop) throws IOException { + File dir = suffix.isEmpty() ? base : new File(base, suffix); + String name = suffix; + + if (dir.isDirectory()) { + if (loop >= 100) { + feedback.printf("Over 100 subdirectories? I'm guessing there's a loop in your directory structure. Skipping: %s\n", suffix); + } else { + for (File f : dir.listFiles()) { + delombok0(base, suffix + (suffix.isEmpty() ? "" : File.separator) + f.getName(), loop + 1); + } + } + } else if (dir.isFile()) { + String extension = getExtension(dir); + if (extension.equals("java")) delombok(base, name); + else if (extension.equals("class")) skipClass(name); + else copy(base, name); + } else if (!dir.exists()) { + feedback.printf("Skipping %s because it does not exist.\n", canonical(dir)); + } else { + feedback.printf("Skipping %s because it is a special file type.\n", canonical(dir)); + } + } + + private void skipClass(String fileName) { + if (verbose) feedback.printf("Skipping class file: %s\n", fileName); + } + + private void copy(File base, String fileName) throws IOException { + if (output == null) { + feedback.printf("Skipping resource file: %s\n", fileName); + return; + } + if (verbose) feedback.printf("Copying resource file: %s\n", fileName); + byte[] b = new byte[65536]; + File outFile = new File(base, fileName); + outFile.getParentFile().mkdirs(); + FileInputStream in = new FileInputStream(outFile); + try { + FileOutputStream out = new FileOutputStream(new File(output, fileName)); + try { + while (true) { + int r = in.read(b); + if (r == -1) break; + out.write(b, 0, r); + } + } finally { + out.close(); + } + } finally { + in.close(); + } + } + + public void delombok(String file, Writer writer) throws IOException { + ParseResult result = parser.parse(file, force); + + result.print(writer); + } + + public void delombok(File base, String fileName) throws IOException { + if (output != null && canonical(base).equals(canonical(output))) throw new IOException( + "DELOMBOK: Output file and input file refer to the same filesystem location. Specify a separate path for output."); + + ParseResult result = parser.parse(new File(base, fileName).getAbsolutePath(), force); + + if (verbose) feedback.printf("File: %s [%s]\n", fileName, result.isChanged() ? "delombok-ed" : "unchanged"); + + Writer rawWriter = output == null ? createStandardOutWriter() : createFileWriter(output, fileName); + BufferedWriter writer = new BufferedWriter(rawWriter); + + try { + result.print(writer); + } finally { + writer.close(); + } + } + + private static String canonical(File dir) { + try { + return dir.getCanonicalPath(); + } catch (Exception e) { + return dir.getAbsolutePath(); + } + } + + private static String getExtension(File dir) { + String name = dir.getName(); + int idx = name.lastIndexOf('.'); + return idx == -1 ? "" : name.substring(idx+1); + } + + private Writer createFileWriter(File base, String fileName) throws IOException { + File outFile = new File(base, fileName); + outFile.getParentFile().mkdirs(); + FileOutputStream out = new FileOutputStream(outFile); + return new OutputStreamWriter(out, charset); + } + + private Writer createStandardOutWriter() { + return new OutputStreamWriter(System.out, charset); + } +} diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java new file mode 100644 index 00000000..611dd417 --- /dev/null +++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java @@ -0,0 +1,1377 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package lombok.delombok; + +import static com.sun.tools.javac.code.Flags.ANNOTATION; +import static com.sun.tools.javac.code.Flags.ENUM; +import static com.sun.tools.javac.code.Flags.INTERFACE; +import static com.sun.tools.javac.code.Flags.SYNTHETIC; +import static com.sun.tools.javac.code.Flags.StandardFlags; +import static com.sun.tools.javac.code.Flags.VARARGS; + +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; + +import com.sun.source.tree.Tree; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.TypeTags; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeScanner; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCArrayAccess; +import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; +import com.sun.tools.javac.tree.JCTree.JCAssert; +import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCAssignOp; +import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCBreak; +import com.sun.tools.javac.tree.JCTree.JCCase; +import com.sun.tools.javac.tree.JCTree.JCCatch; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCConditional; +import com.sun.tools.javac.tree.JCTree.JCContinue; +import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; +import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; +import com.sun.tools.javac.tree.JCTree.JCErroneous; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCForLoop; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCIf; +import com.sun.tools.javac.tree.JCTree.JCImport; +import com.sun.tools.javac.tree.JCTree.JCInstanceOf; +import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; +import com.sun.tools.javac.tree.JCTree.JCLiteral; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; +import com.sun.tools.javac.tree.JCTree.JCModifiers; +import com.sun.tools.javac.tree.JCTree.JCNewArray; +import com.sun.tools.javac.tree.JCTree.JCNewClass; +import com.sun.tools.javac.tree.JCTree.JCParens; +import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; +import com.sun.tools.javac.tree.JCTree.JCReturn; +import com.sun.tools.javac.tree.JCTree.JCSkip; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCSwitch; +import com.sun.tools.javac.tree.JCTree.JCSynchronized; +import com.sun.tools.javac.tree.JCTree.JCThrow; +import com.sun.tools.javac.tree.JCTree.JCTry; +import com.sun.tools.javac.tree.JCTree.JCTypeApply; +import com.sun.tools.javac.tree.JCTree.JCTypeCast; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; +import com.sun.tools.javac.tree.JCTree.JCUnary; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.JCWhileLoop; +import com.sun.tools.javac.tree.JCTree.JCWildcard; +import com.sun.tools.javac.tree.JCTree.LetExpr; +import com.sun.tools.javac.tree.JCTree.TypeBoundKind; +import com.sun.tools.javac.util.Convert; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; + +/** Prints out a tree as an indented Java source program. + * + * <p><b>This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice.</b> + */ +@SuppressWarnings("all") // Mainly sun code that has other warning settings +public class PrettyCommentsPrinter extends JCTree.Visitor { + + private static final Method GET_TAG_METHOD; + private static final Field TAG_FIELD; + + static { + Method m = null; + Field f = null; + try { + m = JCTree.class.getDeclaredMethod("getTag"); + } + catch (NoSuchMethodException e) { + try { + f = JCTree.class.getDeclaredField("tag"); + } + catch (NoSuchFieldException e1) { + e1.printStackTrace(); + } + } + GET_TAG_METHOD = m; + TAG_FIELD = f; + } + + static int getTag(JCTree tree) { + if (GET_TAG_METHOD != null) { + try { + return (Integer)GET_TAG_METHOD.invoke(tree); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + catch (InvocationTargetException e) { + throw new RuntimeException(e.getCause()); + } + } + try { + return TAG_FIELD.getInt(tree); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private List<Comment> comments; + private final JCCompilationUnit cu; + private boolean newLine = true; + + public PrettyCommentsPrinter(Writer out, JCCompilationUnit cu, List<Comment> comments) { + this.out = out; + this.comments = comments; + this.cu = cu; + } + + private int endPos(JCTree tree) { + return tree.getEndPosition(cu.endPositions); + } + + private void consumeComments(int till) throws IOException { + boolean shouldIndent = !newLine; + while (comments.nonEmpty() && comments.head.pos < till) { + if (newLine) { + align(); + } + print(comments.head.content); + println(); + comments = comments.tail; + } + if (newLine && shouldIndent) { + 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; + + /** The current left margin. + */ + int lmargin = 0; + + /** The enclosing class name. + */ + Name enclClassName; + + /** A hashtable mapping trees to their documentation comments + * (can be null) + */ + Map<JCTree, String> docComments = null; + + /** Align code to be indented to left margin. + */ + void align() throws IOException { + newLine = false; + for (int i = 0; i < lmargin; i++) out.write("\t"); + } + + /** Increase left margin by indentation width. + */ + void indent() { + lmargin++; + } + + /** Decrease left margin by indentation width. + */ + void undent() { + lmargin--; + } + + /** Enter a new precedence level. Emit a `(' if new precedence level + * is less than precedence level so far. + * @param contextPrec The precedence level in force so far. + * @param ownPrec The new precedence level. + */ + void open(int contextPrec, int ownPrec) throws IOException { + if (ownPrec < contextPrec) out.write("("); + } + + /** Leave precedence level. Emit a `(' if inner precedence level + * is less than precedence level we revert to. + * @param contextPrec The precedence level we revert to. + * @param ownPrec The inner precedence level. + */ + void close(int contextPrec, int ownPrec) throws IOException { + if (ownPrec < contextPrec) out.write(")"); + } + + /** Print string, replacing all non-ascii character with unicode escapes. + */ + public void print(Object s) throws IOException { + newLine = false; + out.write(Convert.escapeUnicode(s.toString())); + } + + /** Print new line. + */ + public void println() throws IOException { + newLine = true; + out.write(lineSep); + } + + String lineSep = System.getProperty("line.separator"); + + /************************************************************************** + * Traversal methods + *************************************************************************/ + + /** Exception to propogate IOException through visitXXX methods */ + private static class UncheckedIOException extends Error { + static final long serialVersionUID = -4032692679158424751L; + UncheckedIOException(IOException e) { + super(e.getMessage(), e); + } + } + + /** Visitor argument: the current precedence level. + */ + int prec; + + /** Visitor method: print expression tree. + * @param prec The current precedence level. + */ + public void printExpr(JCTree tree, int prec) throws IOException { + + int prevPrec = this.prec; + try { + this.prec = prec; + if (tree == null) print("/*missing*/"); + else { + consumeComments(tree.pos); + 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; + } + } + + /** Derived visitor method: print expression tree at minimum precedence level + * for expression. + */ + public void printExpr(JCTree tree) throws IOException { + printExpr(tree, TreeInfo.noPrec); + } + + /** Derived visitor method: print statement tree. + */ + 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 + */ + public <T extends JCTree> void printExprs(List<T> trees, String sep) throws IOException { + if (trees.nonEmpty()) { + printExpr(trees.head); + for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) { + print(sep); + printExpr(l.head); + } + } + } + + /** Derived visitor method: print list of expression trees, separated by commas. + */ + public <T extends JCTree> void printExprs(List<T> trees) throws IOException { + printExprs(trees, ", "); + } + + /** Derived visitor method: print list of statements, each on a separate line. + */ + public void printStats(List<? extends JCTree> trees) throws IOException { + for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) { + align(); + printStat(l.head); + println(); + } + } + + /** Print a set of modifiers. + */ + public void printFlags(long flags) throws IOException { + if ((flags & SYNTHETIC) != 0) print("/*synthetic*/ "); + print(TreeInfo.flagNames(flags)); + if ((flags & StandardFlags) != 0) print(" "); + if ((flags & ANNOTATION) != 0) print("@"); + } + + public void printAnnotations(List<JCAnnotation> trees) throws IOException { + for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) { + printStat(l.head); + println(); + align(); + } + } + + /** Print documentation comment, if it exists + * @param tree The tree for which a documentation comment should be printed. + */ + public void printDocComment(JCTree tree) throws IOException { + if (docComments != null) { + String dc = docComments.get(tree); + if (dc != null) { + print("/**"); println(); + int pos = 0; + int endpos = lineEndPos(dc, pos); + while (pos < dc.length()) { + align(); + print(" *"); + if (pos < dc.length() && dc.charAt(pos) > ' ') print(" "); + print(dc.substring(pos, endpos)); println(); + pos = endpos + 1; + endpos = lineEndPos(dc, pos); + } + align(); print(" */"); println(); + align(); + } + } + } +//where + static int lineEndPos(String s, int start) { + int pos = s.indexOf('\n', start); + if (pos < 0) pos = s.length(); + return pos; + } + + /** If type parameter list is non-empty, print it enclosed in "<...>" brackets. + */ + public void printTypeParameters(List<JCTypeParameter> trees) throws IOEx |
