aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/lombok/core/LombokNode.java4
-rw-r--r--src/core/lombok/javac/HandlerLibrary.java8
-rw-r--r--src/core/lombok/javac/JavacASTAdapter.java5
-rw-r--r--src/core/lombok/javac/JavacASTVisitor.java10
-rw-r--r--src/core/lombok/javac/JavacResolution.java480
-rw-r--r--src/core/lombok/javac/TreeMirrorMaker.java50
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java104
-rw-r--r--src/delombok/lombok/delombok/CommentCollectingScanner.java2
-rw-r--r--src/delombok/lombok/delombok/CommentPreservingParser.java170
-rw-r--r--src/delombok/lombok/delombok/Delombok.java190
-rw-r--r--src/delombok/lombok/delombok/DelombokResult.java62
-rw-r--r--src/delombok/lombok/delombok/PrettyCommentsPrinter.java16
-rw-r--r--src/delombok/lombok/delombok/ant/DelombokTask.java51
-rw-r--r--test/core/src/lombok/AbstractRunTests.java9
-rw-r--r--test/core/src/lombok/RunTestsViaDelombok.java5
-rw-r--r--test/pretty/resource/before/Annotation.java1
-rw-r--r--test/transform/resource/after-delombok/ValComplex.java20
-rw-r--r--test/transform/resource/after-delombok/ValErrors.java8
-rw-r--r--test/transform/resource/after-delombok/ValInFor.java20
-rw-r--r--test/transform/resource/after-delombok/ValLessSimple.java26
-rw-r--r--test/transform/resource/after-delombok/ValSimple.java24
-rw-r--r--test/transform/resource/after-delombok/ValWeirdTypes.java47
-rw-r--r--test/transform/resource/before/ValComplex.java20
-rw-r--r--test/transform/resource/before/ValErrors.java9
-rw-r--r--test/transform/resource/before/ValInFor.java20
-rw-r--r--test/transform/resource/before/ValLessSimple.java31
-rw-r--r--test/transform/resource/before/ValSimple.java26
-rw-r--r--test/transform/resource/before/ValWeirdTypes.java54
-rw-r--r--test/transform/resource/messages-delombok/ValErrors.java.messages2
29 files changed, 1254 insertions, 220 deletions
diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java
index 6d0f3147..4a57e080 100644
--- a/src/core/lombok/core/LombokNode.java
+++ b/src/core/lombok/core/LombokNode.java
@@ -78,6 +78,10 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
this.isStructurallySignificant = calculateIsStructurallySignificant(null);
}
+ public A getAst() {
+ return ast;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return String.format("NODE %s (%s) %s%s",
diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java
index bbbdacd0..5b792874 100644
--- a/src/core/lombok/javac/HandlerLibrary.java
+++ b/src/core/lombok/javac/HandlerLibrary.java
@@ -183,7 +183,13 @@ public class HandlerLibrary {
*/
public void callASTVisitors(JavacAST ast) {
for (JavacASTVisitor visitor : visitorHandlers) try {
- ast.traverse(visitor);
+ if (!visitor.isResolutionBased()) ast.traverse(visitor);
+ } catch (Throwable t) {
+ javacError(String.format("Lombok visitor handler %s failed", visitor.getClass()), t);
+ }
+
+ for (JavacASTVisitor visitor : visitorHandlers) try {
+ if (visitor.isResolutionBased()) ast.traverse(visitor);
} catch (Throwable t) {
javacError(String.format("Lombok visitor handler %s failed", visitor.getClass()), t);
}
diff --git a/src/core/lombok/javac/JavacASTAdapter.java b/src/core/lombok/javac/JavacASTAdapter.java
index 41bc46d3..bbdb6876 100644
--- a/src/core/lombok/javac/JavacASTAdapter.java
+++ b/src/core/lombok/javac/JavacASTAdapter.java
@@ -35,6 +35,11 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
*/
public class JavacASTAdapter implements JavacASTVisitor {
/** {@inheritDoc} */
+ @Override public boolean isResolutionBased() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
@Override public void visitCompilationUnit(JavacNode top, JCCompilationUnit unit) {}
/** {@inheritDoc} */
diff --git a/src/core/lombok/javac/JavacASTVisitor.java b/src/core/lombok/javac/JavacASTVisitor.java
index 3c5887a7..18376037 100644
--- a/src/core/lombok/javac/JavacASTVisitor.java
+++ b/src/core/lombok/javac/JavacASTVisitor.java
@@ -38,6 +38,12 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
*/
public interface JavacASTVisitor {
/**
+ * If true, you'll be called after all the non-resolution based visitors.
+ * NB: Temporary solution - will be rewritten to a different style altogether in a future release.
+ */
+ boolean isResolutionBased();
+
+ /**
* Called at the very beginning and end.
*/
void visitCompilationUnit(JavacNode top, JCCompilationUnit unit);
@@ -101,6 +107,10 @@ public interface JavacASTVisitor {
private int disablePrinting = 0;
private int indent = 0;
+ @Override public boolean isResolutionBased() {
+ return false;
+ }
+
/**
* @param printContent if true, bodies are printed directly, as java code,
* instead of a tree listing of every AST node inside it.
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
new file mode 100644
index 00000000..6c60b11e
--- /dev/null
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -0,0 +1,480 @@
+package lombok.javac;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.util.ArrayDeque;
+import java.util.Map;
+
+import javax.lang.model.type.TypeKind;
+import javax.tools.DiagnosticListener;
+
+import com.sun.tools.javac.code.BoundKind;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
+import com.sun.tools.javac.code.Type.CapturedType;
+import com.sun.tools.javac.code.Type.ClassType;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.TypeTags;
+import com.sun.tools.javac.comp.Attr;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Enter;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.MemberEnter;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+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.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Log;
+
+public class JavacResolution {
+ private final Attr attr;
+ private final LogDisabler logDisabler;
+
+ public JavacResolution(Context context) {
+ attr = Attr.instance(context);
+ logDisabler = new LogDisabler(context);
+ }
+ /**
+ * During resolution, the resolver will emit resolution errors, but without appropriate file names and line numbers. If these resolution errors stick around
+ * then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger.
+ */
+ private static final class LogDisabler {
+ private final Log log;
+ private static final Field errWriterField, warnWriterField, noticeWriterField, dumpOnErrorField, promptOnErrorField, diagnosticListenerField;
+ private PrintWriter errWriter, warnWriter, noticeWriter;
+ private Boolean dumpOnError, promptOnError;
+ private DiagnosticListener<?> contextDiagnosticListener, logDiagnosticListener;
+ private final Context context;
+
+ // If this is true, the fields changed. Better to print weird error messages than to fail outright.
+ private static final boolean dontBother;
+
+ static {
+ boolean z;
+ Field a = null, b = null, c = null, d = null, e = null, f = null;
+ try {
+ a = Log.class.getDeclaredField("errWriter");
+ b = Log.class.getDeclaredField("warnWriter");
+ c = Log.class.getDeclaredField("noticeWriter");
+ d = Log.class.getDeclaredField("dumpOnError");
+ e = Log.class.getDeclaredField("promptOnError");
+ f = Log.class.getDeclaredField("diagListener");
+ z = false;
+ a.setAccessible(true);
+ b.setAccessible(true);
+ c.setAccessible(true);
+ d.setAccessible(true);
+ e.setAccessible(true);
+ f.setAccessible(true);
+ } catch (Exception x) {
+ z = true;
+ }
+
+ errWriterField = a;
+ warnWriterField = b;
+ noticeWriterField = c;
+ dumpOnErrorField = d;
+ promptOnErrorField = e;
+ diagnosticListenerField = f;
+ dontBother = z;
+ }
+
+ LogDisabler(Context context) {
+ this.log = Log.instance(context);
+ this.context = context;
+ }
+
+ boolean disableLoggers() {
+ contextDiagnosticListener = context.get(DiagnosticListener.class);
+ context.put(DiagnosticListener.class, (DiagnosticListener<?>) null);
+ if (dontBother) return false;
+ boolean dontBotherInstance = false;
+
+ PrintWriter dummyWriter = new PrintWriter(new OutputStream() {
+ @Override public void write(int b) throws IOException {
+ // Do nothing on purpose
+ }
+ });
+
+ if (!dontBotherInstance) try {
+ errWriter = (PrintWriter) errWriterField.get(log);
+ errWriterField.set(log, dummyWriter);
+ } catch (Exception e) {
+ dontBotherInstance = true;
+ }
+
+ if (!dontBotherInstance) try {
+ warnWriter = (PrintWriter) warnWriterField.get(log);
+ warnWriterField.set(log, dummyWriter);
+ } catch (Exception e) {
+ dontBotherInstance = true;
+ }
+
+ if (!dontBotherInstance) try {
+ noticeWriter = (PrintWriter) noticeWriterField.get(log);
+ noticeWriterField.set(log, dummyWriter);
+ } catch (Exception e) {
+ dontBotherInstance = true;
+ }
+
+ if (!dontBotherInstance) try {
+ dumpOnError = (Boolean) dumpOnErrorField.get(log);
+ dumpOnErrorField.set(log, false);
+ } catch (Exception e) {
+ dontBotherInstance = true;
+ }
+
+ if (!dontBotherInstance) try {
+ promptOnError = (Boolean) promptOnErrorField.get(log);
+ promptOnErrorField.set(log, false);
+ } catch (Exception e) {
+ dontBotherInstance = true;
+ }
+
+ if (!dontBotherInstance) try {
+ logDiagnosticListener = (DiagnosticListener<?>) diagnosticListenerField.get(log);
+ diagnosticListenerField.set(log, null);
+ } catch (Exception e) {
+ dontBotherInstance = true;
+ }
+
+ if (dontBotherInstance) enableLoggers();
+ return !dontBotherInstance;
+ }
+
+ void enableLoggers() {
+ if (contextDiagnosticListener != null) {
+ context.put(DiagnosticListener.class, contextDiagnosticListener);
+ contextDiagnosticListener = null;
+ }
+ if (errWriter != null) try {
+ errWriterField.set(log, errWriter);
+ errWriter = null;
+ } catch (Exception e) {}
+
+ if (warnWriter != null) try {
+ warnWriterField.set(log, warnWriter);
+ warnWriter = null;
+ } catch (Exception e) {}
+
+ if (noticeWriter != null) try {
+ noticeWriterField.set(log, noticeWriter);
+ noticeWriter = null;
+ } catch (Exception e) {}
+
+ if (dumpOnError != null) try {
+ dumpOnErrorField.set(log, dumpOnError);
+ dumpOnError = null;
+ } catch (Exception e) {}
+
+ if (promptOnError != null) try {
+ promptOnErrorField.set(log, promptOnError);
+ promptOnError = null;
+ } catch (Exception e) {}
+
+ if (logDiagnosticListener != null) try {
+ diagnosticListenerField.set(log, logDiagnosticListener);
+ logDiagnosticListener = null;
+ } catch (Exception e) {}
+ }
+ }
+
+ private static final class EnvFinder extends JCTree.Visitor {
+ private Env<AttrContext> env = null;
+ private Enter enter;
+ private MemberEnter memberEnter;
+ private JCTree copyAt = null;
+
+ EnvFinder(Context context) {
+ this.enter = Enter.instance(context);
+ this.memberEnter = MemberEnter.instance(context);
+ }
+
+ Env<AttrContext> get() {
+ return env;
+ }
+
+ JCTree copyAt() {
+ return copyAt;
+ }
+
+ @Override public void visitTopLevel(JCCompilationUnit tree) {
+ if (copyAt != null) return;
+ env = enter.getTopLevelEnv(tree);
+ }
+
+ @Override public void visitClassDef(JCClassDecl tree) {
+ if (copyAt != null) return;
+ // The commented out one leaves the 'lint' field unset, which causes NPEs during attrib. So, we use the other one.
+ //env = enter.classEnv((JCClassDecl) tree, env);
+ env = enter.getClassEnv(tree.sym);
+ }
+
+ @Override public void visitMethodDef(JCMethodDecl tree) {
+ if (copyAt != null) return;
+ env = memberEnter.getMethodEnv(tree, env);
+ copyAt = tree;
+ }
+
+ public void visitVarDef(JCVariableDecl tree) {
+ if (copyAt != null) return;
+ env = memberEnter.getInitEnv(tree, env);
+ copyAt = tree;
+ }
+
+ @Override public void visitBlock(JCBlock tree) {
+ if (copyAt != null) return;
+ copyAt = tree;
+ }
+ }
+
+// /**
+// * The {@code Env} object primarily tracks legal symbol names. i.e. its the lexical scope. To build it, we need to go from the top and drill down to the current node,
+// * updating the {@code Env} object at each step. This TreeVisitor does that. Requires {@code enterTrees} to be called first (this is done before processors run, normally).
+// */
+// private static final class EnvChainer extends JCTree.Visitor {
+// private Env<AttrContext> env = null;
+// private Enter enter;
+// private MemberEnter memberEnter;
+// private Attr attr;
+// private JCTree target;
+// private boolean blocksAreInitializers;
+//
+// EnvChainer(Context context) {
+// this.enter = Enter.instance(context);
+// this.memberEnter = MemberEnter.instance(context);
+// this.attr = Attr.instance(context);
+// }
+//
+// Env<AttrContext> get() {
+// return env;
+// }
+//
+// @Override public void visitTopLevel(JCCompilationUnit tree) {
+// env = enter.getTopLevelEnv(tree);
+// }
+//
+// @Override public void visitClassDef(JCClassDecl tree) {
+// // The commented out one leaves the 'lint' field unset, which causes NPEs during attrib. So, we use the other one.
+// //env = enter.classEnv((JCClassDecl) tree, env);
+// env = enter.getClassEnv(tree.sym);
+// blocksAreInitializers = true;
+// }
+//
+// @Override public void visitMethodDef(JCMethodDecl tree) {
+// env = memberEnter.getMethodEnv(tree, env);
+// blocksAreInitializers = false;
+// if (tree.body != null) visitBlock(tree.body);
+// }
+//
+// @Override public void visitBlock(JCBlock tree) {
+// if (blocksAreInitializers) attr.attribStat(tree, env);
+// for (JCStatement stat : tree.stats) {
+// if (stat == target) return;
+// attr.attribStat(stat, env);
+// }
+// }
+//
+// @Override public void visitTree(JCTree tree) {
+// // Do nothing
+// }
+//
+// public void setTarget(JCTree target) {
+// this.target = target;
+// }
+// };
+
+ public Map<JCTree, JCTree> resolve(JavacNode node) {
+ ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>();
+
+ {
+ JavacNode n = node;
+ while (n != null) {
+ stack.push(n.get());
+ n = n.up();
+ }
+ }
+
+ logDisabler.disableLoggers();
+ try {
+ EnvFinder finder = new EnvFinder(node.getContext());
+ while (!stack.isEmpty()) stack.pop().accept(finder);
+
+ TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node);
+ JCTree copy = mirrorMaker.copy(finder.copyAt());
+
+ attrib(copy, finder.get());
+ return mirrorMaker.getOriginalToCopyMap();
+ } finally {
+ logDisabler.enableLoggers();
+ }
+ }
+
+ private void attrib(JCTree tree, Env<AttrContext> env) {
+ if (tree instanceof JCBlock) attr.attribStat(tree, env);
+ else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl)tree).body, env);
+ else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env);
+ else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl");
+ }
+
+// public void resolveUpTo(JavacNode statementNode) {
+// ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>();
+//
+// {
+// JavacNode n = statementNode;
+// while (n != null) {
+// stack.push(n.get());
+// n = n.up();
+// }
+// }
+//
+// logDisabler.disableLoggers();
+// try {
+// JCTree tree = stack.isEmpty() ? null : stack.pop();
+// while (!stack.isEmpty()) {
+// JCTree target = stack.pop();
+// envChainer.setTarget(target);
+// tree.accept(envChainer);
+// tree = target;
+// }
+// if (tree != null) {
+// envChainer.setTarget(null);
+// tree.accept(envChainer);
+// }
+//
+//// System.out.println("ATTRIBSTAT: " + attr.attribStat(statementNode.get(), envChainer.get()));
+//// if (statementNode.get() instanceof JCVariableDecl) {
+//// System.out.println("Force-tribbing expr");
+//// JCExpression init = ((JCVariableDecl)statementNode.get()).init;
+//// System.out.println("ATTRIBEXPR: " + attr.attribExpr(init, envChainer.get(), Type.noType));
+//// System.out.println("TYPE: " + ((JCVariableDecl)statementNode.get()).init.type);
+//// }
+// } finally {
+// logDisabler.enableLoggers();
+// }
+// }
+//
+// public void resolveExpr(JCExpression expr) {
+// logDisabler.disableLoggers();
+// try {
+// attr.attribExpr(expr, envChainer.get(), Type.noType);
+// } finally {
+// logDisabler.enableLoggers();
+// }
+// }
+
+ public static class TypeNotConvertibleException extends Exception {
+ public TypeNotConvertibleException(String msg) {
+ super(msg);
+ }
+ }
+
+ public static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast) throws TypeNotConvertibleException {
+ return typeToJCTree(type, maker, ast, false);
+ }
+
+ public static JCExpression createJavaLangObject(TreeMaker maker, JavacAST ast) {
+ JCExpression out = maker.Ident(ast.toName("java"));
+ out = maker.Select(out, ast.toName("lang"));
+ out = maker.Select(out, ast.toName("Object"));
+ return out;
+ }
+
+ private static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound) throws TypeNotConvertibleException {
+ // NB: There's such a thing as maker.Type(type), but this doesn't work very well; it screws up anonymous classes, captures, and adds an extra prefix dot for some reason too.
+ // -- so we write our own take on that here.
+
+ if (type.isPrimitive()) return primitiveToJCTree(type.getKind(), maker);
+ if (type.isErroneous()) throw new TypeNotConvertibleException("Type cannot be resolved");
+
+ TypeSymbol symbol = type.asElement();
+ List<Type> generics = type.getTypeArguments();
+
+ JCExpression replacement = null;
+
+ if (symbol == null) throw new TypeNotConvertibleException("Null or compound type");
+
+ if (symbol.name.len == 0) {
+ // Anonymous inner class
+ if (type instanceof ClassType) {
+ List<Type> ifaces = ((ClassType)type).interfaces_field;
+ Type supertype = ((ClassType)type).supertype_field;
+ if (ifaces != null && ifaces.length() == 1) {
+ return typeToJCTree(ifaces.get(0), maker, ast, allowCompound);
+ }
+ if (supertype != null) return typeToJCTree(supertype, maker, ast, allowCompound);
+ }
+ throw new TypeNotConvertibleException("Anonymous inner class");
+ }
+
+ if (type instanceof CapturedType) {
+ if (allowCompound) {
+ if (type.getLowerBound() == null || type.getLowerBound().tag == TypeTags.BOT) {
+ if (type.getUpperBound().toString().equals("java.lang.Object")) {
+ return maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ }
+ return maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), typeToJCTree(type.getUpperBound(), maker, ast, false));
+ } else {
+ return maker.Wildcard(maker.TypeBoundKind(BoundKind.SUPER), typeToJCTree(type.getLowerBound(), maker, ast, false));
+ }
+ }
+ if (type.getUpperBound() != null) {
+ return typeToJCTree(type.getUpperBound(), maker, ast, allowCompound);
+ }
+
+ return createJavaLangObject(maker, ast);
+ }
+
+ String qName = symbol.getQualifiedName().toString();
+ if (qName.isEmpty()) throw new TypeNotConvertibleException("unknown type");
+ if (qName.startsWith("<")) throw new TypeNotConvertibleException(qName);
+ String[] baseNames = symbol.getQualifiedName().toString().split("\\.");
+ replacement = maker.Ident(ast.toName(baseNames[0]));
+ for (int i = 1; i < baseNames.length; i++) {
+ replacement = maker.Select(replacement, ast.toName(baseNames[i]));
+ }
+
+ if (generics != null && !generics.isEmpty()) {
+ List<JCExpression> args = List.nil();
+ for (Type t : generics) args = args.append(typeToJCTree(t, maker, ast, true));
+ replacement = maker.TypeApply(replacement, args);
+ }
+
+ return replacement;
+ }
+
+ private static JCExpression primitiveToJCTree(TypeKind kind, TreeMaker maker) throws TypeNotConvertibleException {
+ switch (kind) {
+ case BYTE:
+ return maker.TypeIdent(TypeTags.BYTE);
+ case CHAR:
+ return maker.TypeIdent(TypeTags.CHAR);
+ case SHORT:
+ return maker.TypeIdent(TypeTags.SHORT);
+ case INT:
+ return maker.TypeIdent(TypeTags.INT);
+ case LONG:
+ return maker.TypeIdent(TypeTags.LONG);
+ case FLOAT:
+ return maker.TypeIdent(TypeTags.FLOAT);
+ case DOUBLE:
+ return maker.TypeIdent(TypeTags.DOUBLE);
+ case BOOLEAN:
+ return maker.TypeIdent(TypeTags.BOOLEAN);
+ case VOID:
+ return maker.TypeIdent(TypeTags.VOID);
+ case NULL:
+ case NONE:
+ case OTHER:
+ default:
+ throw new TypeNotConvertibleException("Nulltype");
+ }
+ }
+}
diff --git a/src/core/lombok/javac/TreeMirrorMaker.java b/src/core/lombok/javac/TreeMirrorMaker.java
new file mode 100644
index 00000000..1c0b9311
--- /dev/null
+++ b/src/core/lombok/javac/TreeMirrorMaker.java
@@ -0,0 +1,50 @@
+package lombok.javac;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeCopier;
+import com.sun.tools.javac.util.List;
+
+public class TreeMirrorMaker extends TreeCopier<Void> {
+ private final IdentityHashMap<JCTree, JCTree> originalToCopy = new IdentityHashMap<JCTree, JCTree>();
+
+ public TreeMirrorMaker(JavacNode node) {
+ super(node.getTreeMaker());
+ }
+
+ @Override public <T extends JCTree> T copy(T original) {
+ T copy = super.copy(original);
+ originalToCopy.put(original, copy);
+ return copy;
+ }
+
+ @Override public <T extends JCTree> T copy(T original, Void p) {
+ T copy = super.copy(original, p);
+ originalToCopy.put(original, copy);
+ return copy;
+ }
+
+ @Override public <T extends JCTree> List<T> copy(List<T> originals) {
+ List<T> copies = super.copy(originals);
+ Iterator<T> it1 = originals.iterator();
+ Iterator<T> it2 = copies.iterator();
+ while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next());
+ return copies;
+ }
+
+ @Override public <T extends JCTree> List<T> copy(List<T> originals, Void p) {
+ List<T> copies = super.copy(originals, p);
+ Iterator<T> it1 = originals.iterator();
+ Iterator<T> it2 = copies.iterator();
+ while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next());
+ return copies;
+ }
+
+ public Map<JCTree, JCTree> getOriginalToCopyMap() {
+ return Collections.unmodifiableMap(originalToCopy);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
new file mode 100644
index 00000000..a6f22093
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2010 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.javac.handlers;
+
+import lombok.javac.JavacASTAdapter;
+import lombok.javac.JavacASTVisitor;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacResolution;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+
+@ProviderFor(JavacASTVisitor.class)
+public class HandleVal extends JavacASTAdapter {
+ @Override public boolean isResolutionBased() {
+ return true;
+ }
+
+ @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) {
+ if (local.vartype != null && local.vartype.toString().equals("val")) {
+ JCExpression rhsOfEnhancedForLoop = null;
+ if (local.init == null) {
+ JCTree parentRaw = localNode.directUp().get();
+ if (parentRaw instanceof JCEnhancedForLoop) {
+ JCEnhancedForLoop efl = (JCEnhancedForLoop) parentRaw;
+ if (efl.var == local) rhsOfEnhancedForLoop = efl.expr;
+ }
+ }
+
+ if (rhsOfEnhancedForLoop == null && local.init == null) {
+ localNode.addError("'val' on a local variable requires an initializer expression");
+ return;
+ }
+
+ local.mods.flags |= Flags.FINAL;
+ JCExpression oldVarType = local.vartype;
+ local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());
+
+ Type type;
+ try {
+ if (rhsOfEnhancedForLoop == null) {
+ if (local.init.type == null) {
+ JavacResolution resolver = new JavacResolution(localNode.getContext());
+ type = ((JCExpression) resolver.resolve(localNode).get(local.init)).type;
+ } else {
+ type = local.init.type;
+ }
+ } else {
+ if (rhsOfEnhancedForLoop.type == null) {
+ JavacResolution resolver = new JavacResolution(localNode.getContext());
+ type = ((JCExpression) resolver.resolve(localNode.directUp()).get(rhsOfEnhancedForLoop)).type;
+ } else {
+ type = rhsOfEnhancedForLoop.type;
+ }
+ }
+
+ try {
+ JCExpression replacement;
+
+ if (rhsOfEnhancedForLoop != null) {
+ localNode.addError("For now 'val' cannot be used in enhanced for loops. Fixing this is a high priority lombok issue though!");
+ return;
+ // TODO: Fix enhanced for loops - then uncomment a bunch of lines in test/transform/resource/*/ValInFor.java
+ }
+ replacement = JavacResolution.typeToJCTree(type, localNode.getTreeMaker(), localNode.getAst());
+ if (replacement != null) local.vartype = replacement;
+ else local.vartype = oldVarType;
+ } catch (JavacResolution.TypeNotConvertibleException e) {
+ localNode.addError("Cannot use 'val' here because initializer expression does not have a representable type: " + e.getMessage());
+ local.vartype = oldVarType;
+ }
+ } catch (RuntimeException e) {
+ local.vartype = oldVarType;
+ throw e;
+ }
+ }
+ // TODO search for val decls in either kind of for.
+ }
+}
diff --git a/src/delombok/lombok/delombok/CommentCollectingScanner.java b/src/delombok/lombok/delombok/CommentCollectingScanner.java
index 2552cbf7..c930bc62 100644
--- a/src/delombok/lombok/delombok/CommentCollectingScanner.java
+++ b/src/delombok/lombok/delombok/CommentCollectingScanner.java
@@ -25,7 +25,7 @@ import java.nio.CharBuffer;
import lombok.delombok.Comment.EndConnection;
import lombok.delombok.Comment.StartConnection;
-import lombok.delombok.CommentPreservingParser.Comments;
+import lombok.delombok.Delombok.Comments;
import com.sun.tools.javac.parser.Scanner;
import com.sun.tools.javac.util.Context;
diff --git a/src/delombok/lombok/delombok/CommentPreservingParser.java b/src/delombok/lombok/delombok/CommentPreservingParser.java
deleted file mode 100644
index 87c02dcd..00000000
--- a/src/delombok/lombok/delombok/CommentPreservingParser.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright © 2009-2010 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.DiagnosticListener;
-import javax.tools.JavaFileObject;
-import javax.tools.Diagnostic.Kind;
-
-import lombok.javac.DeleteLombokAnnotations;
-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;
- private boolean deleteLombokAnnotations = false;
- private DiagnosticListener<JavaFileObject> diagnostics = null;
-
- public CommentPreservingParser() {
- this("utf-8");
- }
-
- public CommentPreservingParser(String encoding) {
- this.encoding = encoding;
- }
-
- public void setDeleteLombokAnnotations(boolean deleteLombokAnnotations) {
- this.deleteLombokAnnotations = deleteLombokAnnotations;
- }
-
- public void setDiagnosticsListener(DiagnosticListener<JavaFileObject> diagnostics) {
- this.diagnostics = diagnostics;
- }
-
- public ParseResult parse(JavaFileObject source, boolean forceProcessing) throws IOException {
- return doParse(source, forceProcessing);
- }
-
- public ParseResult parse(String fileName, boolean forceProcessing) throws IOException {
- return doParse(fileName, forceProcessing);
- }
-
- private ParseResult doParse(Object source, boolean forceProcessing) throws IOException {
- Context context = new Context();
-
- Options.instance(context).put(OptionName.ENCODING, encoding);
-
- if (diagnostics != null) context.put(DiagnosticListener.class, diagnostics);
-
- 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);
- if (deleteLombokAnnotations) context.put(DeleteLombokAnnotations.class, new DeleteLombokAnnotations(true));
-
- comments.comments = List.nil();
-
- JCCompilationUnit cu;
- if (source instanceof JavaFileObject) {
- cu = compiler.parse((JavaFileObject) source);
- } else {
- @SuppressWarnings("deprecation")
- JCCompilationUnit unit = compiler.parse((String)source);
- cu = unit;
- }
-
- 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(Comment comment) {
- comments = comments.append(comment);
- }
- }
-
- 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 ");
- out.write(String.valueOf(new Date()));
- out.write(System.getProperty("line.separator"));
-
- 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
index 56994f78..6866d97f 100644
--- a/src/delombok/lombok/delombok/Delombok.java
+++ b/src/delombok/lombok/delombok/Delombok.java
@@ -30,16 +30,33 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;
+import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+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.DiagnosticListener;
import javax.tools.JavaFileObject;
+import javax.tools.Diagnostic.Kind;
-import lombok.delombok.CommentPreservingParser.ParseResult;
+import lombok.javac.DeleteLombokAnnotations;
+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.Options;
import com.zwitserloot.cmdreader.CmdReader;
import com.zwitserloot.cmdreader.Description;
import com.zwitserloot.cmdreader.Excludes;
@@ -50,16 +67,24 @@ import com.zwitserloot.cmdreader.Shorthand;
public class Delombok {
private Charset charset = Charset.defaultCharset();
- private CommentPreservingParser parser = new CommentPreservingParser();
+ private Context context = new Context();
+ private Writer presetWriter;
- {
- parser.setDeleteLombokAnnotations(true);
+ public void setWriter(Writer writer) {
+ this.presetWriter = writer;
+ }
+
+ public Delombok() {
+ context.put(DeleteLombokAnnotations.class, new DeleteLombokAnnotations(true));
}
private PrintStream feedback = System.err;
private boolean verbose;
private boolean noCopy;
private boolean force = false;
+ private String classpath, sourcepath;
+ private LinkedHashMap<File, File> fileToBase = new LinkedHashMap<File, File>();
+ private List<File> filesToParse = new ArrayList<File>();
/** If null, output to standard out. */
private File output = null;
@@ -88,6 +113,14 @@ public class Delombok {
@Mandatory(onlyIfNot={"print", "help"})
private String target;
+ @Shorthand("c")
+ @Description("Classpath (analogous to javac -cp option)")
+ private String classpath;
+
+ @Shorthand("s")
+ @Description("Sourcepath (analogous to javac -sourcepath option)")
+ private String sourcepath;
+
@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
private List<String> input = new ArrayList<String>();
@@ -138,21 +171,29 @@ public class Delombok {
if (args.verbose) delombok.setVerbose(true);
if (args.nocopy) delombok.setNoCopy(true);
- if (args.print) delombok.setOutputToStandardOut();
- else delombok.setOutput(new File(args.target));
+ if (args.print) {
+ delombok.setOutputToStandardOut();
+ } else {
+ delombok.setOutput(new File(args.target));
+ }
+
+ if (args.classpath != null) delombok.setClasspath(args.classpath);
+ if (args.sourcepath != null) delombok.setSourcepath(args.sourcepath);
for (String in : args.input) {
try {
File f = new File(in);
if (f.isFile()) {
- delombok.delombok(f.getParentFile(), f.getName());
+ delombok.addFile(f.getParentFile(), f.getName());
} else if (f.isDirectory()) {
- delombok.delombok(f);
+ delombok.addDirectory(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);
}
+
+ delombok.delombok();
} catch (Exception e) {
if (!args.quiet) {
String msg = e.getMessage();
@@ -168,12 +209,12 @@ public class Delombok {
}
public void setCharset(String charsetName) throws UnsupportedCharsetException {
+
charset = Charset.forName(charsetName);
}
-
public void setDiagnosticsListener(DiagnosticListener<JavaFileObject> diagnostics) {
- parser.setDiagnosticsListener(diagnostics);
+ if (diagnostics != null) context.put(DiagnosticListener.class, diagnostics);
}
public void setForceProcess(boolean force) {
@@ -184,6 +225,14 @@ public class Delombok {
this.feedback = feedback;
}
+ public void setClasspath(String classpath) {
+ this.classpath = classpath;
+ }
+
+ public void setSourcepath(String sourcepath) {
+ this.sourcepath = sourcepath;
+ }
+
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
@@ -204,15 +253,15 @@ public class Delombok {
this.output = null;
}
- public void delombok(File base) throws IOException {
- delombok0(false, base, "", 0);
+ public void addDirectory(File base) throws IOException {
+ addDirectory0(false, base, "", 0);
}
- public void process(boolean copy, File base, String name) throws IOException {
+ public void addDirectory1(boolean copy, File base, String name) throws IOException {
File f = new File(base, name);
if (f.isFile()) {
String extension = getExtension(f);
- if (extension.equals("java")) delombok(base, name);
+ if (extension.equals("java")) addFile(base, name);
else if (extension.equals("class")) skipClass(name);
else copy(copy, base, name);
} else if (!f.exists()) {
@@ -222,7 +271,7 @@ public class Delombok {
}
}
- private void delombok0(boolean inHiddenDir, File base, String suffix, int loop) throws IOException {
+ private void addDirectory0(boolean inHiddenDir, File base, String suffix, int loop) throws IOException {
File dir = suffix.isEmpty() ? base : new File(base, suffix);
if (dir.isDirectory()) {
@@ -236,7 +285,7 @@ public class Delombok {
feedback.printf("Only processing java files (not copying non-java files) in %s because it's a hidden directory.\n", canonical(dir));
}
for (File f : list) {
- delombok0(inHiddenDir || thisDirIsHidden, base, suffix + (suffix.isEmpty() ? "" : File.separator) + f.getName(), loop + 1);
+ addDirectory0(inHiddenDir || thisDirIsHidden, base, suffix + (suffix.isEmpty() ? "" : File.separator) + f.getName(), loop + 1);
}
} else {
if (!thisDirIsHidden && !noCopy && !inHiddenDir && output != null && !suffix.isEmpty()) {
@@ -247,7 +296,7 @@ public class Delombok {
}
}
} else {
- process(!inHiddenDir && !noCopy, base, suffix);
+ addDirectory1(!inHiddenDir && !noCopy, base, suffix);
}
}
@@ -288,34 +337,96 @@ public class Delombok {
}
}
- public void delombok(JavaFileObject file, Writer writer) throws IOException {
- ParseResult result = parser.parse(file, force);
- result.print(writer);
+ public void addFile(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.");
+
+ File f = new File(base, fileName);
+ filesToParse.add(f);
+ fileToBase.put(f, base);
}
- public void delombok(String file, Writer writer) throws IOException {
- ParseResult result = parser.parse(file, force);
- result.print(writer);
+ private static <T> com.sun.tools.javac.util.List<T> toJavacList(List<T> list) {
+ com.sun.tools.javac.util.List<T> out = com.sun.tools.javac.util.List.nil();
+ ListIterator<T> li = list.listIterator(list.size());
+ while (li.hasPrevious()) out = out.prepend(li.previous());
+ return out;
}
- 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.");
+ public boolean delombok() throws IOException {
+ Options options = Options.instance(context);
+ options.put(OptionName.ENCODING, charset.name());
+ if (classpath != null) options.put(OptionName.CLASSPATH, classpath);
+ if (sourcepath != null) options.put(OptionName.SOURCEPATH, sourcepath);
+ CommentCollectingScanner.Factory.preRegister(context);
- ParseResult result = parser.parse(new File(base, fileName).getAbsolutePath(), force);
+ JavaCompiler compiler = new JavaCompiler(context);
+ compiler.keepComments = true;
+ compiler.genEndPos = true;
- if (verbose) feedback.printf("File: %s [%s]\n", fileName, result.isChanged() ? "delombok-ed" : "unchanged");
+ List<JCCompilationUnit> roots = new ArrayList<JCCompilationUnit>();
+ Map<JCCompilationUnit, Comments> commentsMap = new IdentityHashMap<JCCompilationUnit, Comments>();
+ Map<JCCompilationUnit, File> baseMap = new IdentityHashMap<JCCompilationUnit, File>();
+ for (File fileToParse : filesToParse) {
+ Comments comments = new Comments();
+ context.put(Comments.class, comments);
+
+ @SuppressWarnings("deprecation")
+ JCCompilationUnit unit = compiler.parse(fileToParse.getAbsolutePath());
+
+ commentsMap.put(unit, comments);
+ baseMap.put(unit, fileToBase.get(fileToParse));
+ roots.add(unit);
+ }
- Writer rawWriter = output == null ? createStandardOutWriter() : createFileWriter(output, fileName);
- BufferedWriter writer = new BufferedWriter(rawWriter);
+ if (compiler.errorCount() > 0) return false;
+ compiler.enterTrees(toJavacList(roots));
- try {
- result.print(writer);
- } finally {
- writer.close();
+ for (JCCompilationUnit unit : roots) {
+ boolean changed = new JavacTransformer(messager).transform(context, Collections.singleton(unit));
+ DelombokResult result = new DelombokResult(commentsMap.get(unit).comments, unit, force || changed);
+ if (verbose) feedback.printf("File: %s [%s]\n", unit.sourcefile.getName(), result.isChanged() ? "delomboked" : "unchanged");
+ Writer rawWriter;
+ if (presetWriter != null) rawWriter = presetWriter;
+ else if (output == null) rawWriter = createStandardOutWriter();
+ else rawWriter = createFileWriter(output, baseMap.get(unit), unit.sourcefile.toUri());
+ BufferedWriter writer = new BufferedWriter(rawWriter);
+ try {
+ result.print(writer);
+ } finally {
+ writer.close();
+ }
+ }
+
+ return true;
+ }
+
+ public static class Comments {
+ public com.sun.tools.javac.util.List<Comment> comments = com.sun.tools.javac.util.List.nil();
+
+ void add(Comment comment) {
+ comments = comments.append(comment);
}
}
+ 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);
+ }
+ };
+
private static String canonical(File dir) {
try {
return dir.getCanonicalPath();
@@ -330,8 +441,15 @@ public class Delombok {
return idx == -1 ? "" : name.substring(idx+1);
}
- private Writer createFileWriter(File base, String fileName) throws IOException {
- File outFile = new File(base, fileName);
+ private Writer createFileWriter(File outBase, File inBase, URI file) throws IOException {
+ URI relative = inBase.toURI().relativize(file);
+ File outFile;
+ if (relative.isAbsolute()) {
+ outFile = new File(outBase, new File(relative).getName());
+ } else {
+ outFile = new File(outBase, relative.getPath());
+ }
+
outFile.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(outFile);
return createUnicodeEscapeWriter(out);
diff --git a/src/delombok/lombok/delombok/DelombokResult.java b/src/delombok/lombok/delombok/DelombokResult.java
new file mode 100644
index 00000000..717a3bf1
--- /dev/null
+++ b/src/delombok/lombok/delombok/DelombokResult.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2009-2010 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.Date;
+
+import javax.tools.JavaFileObject;
+
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.util.List;
+
+public class DelombokResult {
+ private final List<Comment> comments;
+ private final JCCompilationUnit compilationUnit;
+ private final boolean changed;
+
+ public DelombokResult(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 ");
+ out.write(String.valueOf(new Date()));
+ out.write(System.getProperty("line.separator"));
+
+ compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments));
+ }
+
+ public boolean isChanged() {
+ return changed;
+ }
+} \ No newline at end of file
diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
index 55c0e1bc..16c06f50 100644
--- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
+++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
@@ -670,9 +670,11 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
public void visitMethodDef(JCMethodDecl tree) {
try {
+ boolean isConstructor = tree.name == tree.name.table.fromChars("<init>".toCharArray(), 0, 6);
// when producing source output, omit anonymous constructors
- if (tree.name == tree.name.table.fromChars("<init>".toCharArray(), 0, 6) &&
- enclClassName == null) return;
+ if (isConstructor && enclClassName == null) return;
+ boolean isGeneratedConstructor = isConstructor && ((tree.mods.flags & Flags.GENERATEDCONSTR) != 0);
+ if (isGeneratedConstructor) return;
println(); align();
printDocComment(tree);
printExpr(tree.mods);
@@ -1442,9 +1444,13 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
print("@");
printExpr(tree.annotationType);
if (tree.args.nonEmpty()) {
- print("(");
- printExprs(tree.args);
- print(")");
+ print("(");
+ if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
+ JCExpression lhs = ((JCAssign)tree.args.get(0)).lhs;
+ if (lhs instanceof JCIdent && ((JCIdent)lhs).name.toString().equals("value")) tree.args = List.of(((JCAssign)tree.args.get(0)).rhs);
+ }
+ printExprs(tree.args);
+ print(")");
}
} catch (IOException e) {
throw new UncheckedIOException(e);
diff --git a/src/delombok/lombok/delombok/ant/DelombokTask.java b/src/delombok/lombok/delombok/ant/DelombokTask.java
index 4a40b874..40c3c63e 100644
--- a/src/delombok/lombok/delombok/ant/DelombokTask.java
+++ b/src/delombok/lombok/delombok/ant/DelombokTask.java
@@ -32,14 +32,55 @@ import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.resources.FileResource;
public class DelombokTask extends Task {
private File fromDir, toDir;
+ private Path classpath;
+ private Path sourcepath;
private boolean verbose;
private String encoding;
private Path path;
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ return classpath.createPath();
+ }
+
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ public void setSourcepath(Path sourcepath) {
+ if (this.sourcepath == null) {
+ this.sourcepath = sourcepath;
+ } else {
+ this.sourcepath.append(sourcepath);
+ }
+ }
+
+ public Path createSourcepath() {
+ if (sourcepath == null) {
+ sourcepath = new Path(getProject());
+ }
+ return sourcepath.createPath();
+ }
+
+ public void setSourcepathRef(Reference r) {
+ createSourcepath().setRefid(r);
+ }
+
public void setFrom(File dir) {
this.fromDir = dir;
}
@@ -75,9 +116,12 @@ public class DelombokTask extends Task {
throw new BuildException("Unknown charset: " + encoding, getLocation());
}
+ if (classpath != null) delombok.setClasspath(classpath.toString());
+ if (sourcepath != null) delombok.setSourcepath(sourcepath.toString());
+
delombok.setOutput(toDir);
try {
- if (fromDir != null) delombok.delombok(fromDir);
+ if (fromDir != null) delombok.addDirectory(fromDir);
else {
Iterator<?> it = path.iterator();
while (it.hasNext()) {
@@ -85,12 +129,13 @@ public class DelombokTask extends Task {
File baseDir = fileResource.getBaseDir();
if (baseDir == null) {
File file = fileResource.getFile();
- delombok.process(false, file.getParentFile(), file.getName());
+ delombok.addFile(file.getParentFile(), file.getName());
} else {
- delombok.process(false, baseDir, fileResource.getName());
+ delombok.addFile(baseDir, fileResource.getName());
}
}
}
+ delombok.delombok();
} catch (IOException e) {
throw new BuildException("I/O problem during delombok", e, getLocation());
}
diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java
index 4241646b..7d5992be 100644
--- a/test/core/src/lombok/AbstractRunTests.java
+++ b/test/core/src/lombok/AbstractRunTests.java
@@ -37,12 +37,12 @@ public abstract class AbstractRunTests {
public void compareFile(DirectoryRunner.TestParams params, File file) throws Throwable {
StringBuilder messages = new StringBuilder();
- StringWriter result = new StringWriter();
- transformCode(messages, result, file);
+ StringWriter writer = new StringWriter();
+ transformCode(messages, writer, file);
compare(
file.getName(),
readFile(params.getAfterDirectory(), file, false),
- result.toString(),
+ writer.toString(),
readFile(params.getMessagesDirectory(), file, true),
messages.toString(),
params.printErrors());
@@ -111,6 +111,9 @@ public abstract class AbstractRunTests {
private static void compareContent(String name, String expectedFile, String actualFile) {
String[] expectedLines = expectedFile.split("(\\r?\\n)");
String[] actualLines = actualFile.split("(\\r?\\n)");
+ if (expectedLines[0].startsWith("// Generated by delombok at ")) {
+ expectedLines[0] = "";
+ }
if (actualLines[0].startsWith("// Generated by delombok at ")) {
actualLines[0] = "";
}
diff --git a/test/core/src/lombok/RunTestsViaDelombok.java b/test/core/src/lombok/RunTestsViaDelombok.java
index 50fad33e..59a0ee89 100644
--- a/test/core/src/lombok/RunTestsViaDelombok.java
+++ b/test/core/src/lombok/RunTestsViaDelombok.java
@@ -51,6 +51,9 @@ public class RunTestsViaDelombok extends AbstractRunTests {
}
});
- delombok.delombok(file.getAbsolutePath(), result);
+ delombok.addFile(file.getParentFile(), file.getName());
+ delombok.setSourcepath(file.getParentFile().getAbsolutePath());
+ delombok.setWriter(result);
+ delombok.delombok();
}
}
diff --git a/test/pretty/resource/before/Annotation.java b/test/pretty/resource/before/Annotation.java
index edd1a5e7..24868acd 100644
--- a/test/pretty/resource/before/Annotation.java
+++ b/test/pretty/resource/before/Annotation.java
@@ -1,3 +1,4 @@
+//ignore
@SuppressWarnings("all")
class Annotation {
@Override
diff --git a/test/transform/resource/after-delombok/ValComplex.java b/test/transform/resource/after-delombok/ValComplex.java
new file mode 100644
index 00000000..54ef5c78
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValComplex.java
@@ -0,0 +1,20 @@
+public class ValComplex {
+ private ValSimple field = new ValSimple();
+ private static final int CONSTANT = 20;
+ public void testReferencingOtherFiles() {
+ final java.lang.String shouldBeString = field.method();
+ final int shouldBeInt = CONSTANT;
+ final java.lang.Object lock = new Object();
+ synchronized (lock) {
+ final int field = 20; //Shadowing
+ final int inner = 10;
+ switch (field) {
+ case 5:
+ final java.lang.String shouldBeString2 = shouldBeString;
+ final int innerInner = inner;
+
+ }
+ }
+ final ValSimple shouldBeValSimple = field; //Unshadowing
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValErrors.java b/test/transform/resource/after-delombok/ValErrors.java
new file mode 100644
index 00000000..5ac785ab
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValErrors.java
@@ -0,0 +1,8 @@
+public class ValErrors {
+ public void nullType() {
+ final val a = null;
+ }
+ public void unresolvableExpression() {
+ final val c = d;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValInFor.java b/test/transform/resource/after-delombok/ValInFor.java
new file mode 100644
index 00000000..d97ce732
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValInFor.java
@@ -0,0 +1,20 @@
+public class ValInFor {
+ {
+ final int x = 10;
+ final int x2 = -1;
+ final java.lang.String a = "Hello";
+ for (final int y = x, z = x2; y < 20; y++) {
+ final int q = y;
+ final int w = z;
+ final java.lang.String v = a;
+ }
+ }
+/* public void enhancedFor() {
+ java.util.List<String> list = java.util.Arrays.asList("Hello, World!");
+ for (val shouldBeString : list) {
+ System.out.println(shouldBeString.toLowerCase());
+ val shouldBeString2 = shouldBeString;
+ }
+ }
+*/
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValLessSimple.java b/test/transform/resource/after-delombok/ValLessSimple.java
new file mode 100644
index 00000000..678b419e
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValLessSimple.java
@@ -0,0 +1,26 @@
+public class ValLessSimple {
+ private short field2 = 5;
+ private String method() {
+ return "method";
+ }
+ private double method2() {
+ return 2.0;
+ }
+ {
+ System.out.println("Hello");
+ final int z = 20;
+ final int x = 10;
+ final int a = z;
+ final short y = field2;
+ }
+ private void testVal(String param) {
+ final java.lang.String fieldV = field;
+ final int a = 10;
+ final int b = 20;
+ {
+ final java.lang.String methodV = method();
+ final java.lang.String foo = fieldV + methodV;
+ }
+ }
+ private String field = "field";
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValSimple.java b/test/transform/resource/after-delombok/ValSimple.java
new file mode 100644
index 00000000..b2783eac
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValSimple.java
@@ -0,0 +1,24 @@
+public class ValSimple {
+ private String field = "field";
+ private short field2 = 5;
+
+ private String method() {
+ return "method";
+ }
+
+ private double method2() {
+ return 2.0;
+ }
+
+ private void testVal(String param) {
+ final java.lang.String fieldV = field;
+ final java.lang.String methodV = method();
+ final java.lang.String paramV = param;
+ final java.lang.String valOfVal = fieldV;
+ final java.lang.String operatorV = fieldV + valOfVal;
+ final short fieldW = field2;
+ final double methodW = method2();
+ byte localVar = 3;
+ final int operatorW = fieldW + localVar;
+ }
+}
diff --git a/test/transform/resource/after-delombok/ValWeirdTypes.java b/test/transform/resource/after-delombok/ValWeirdTypes.java
new file mode 100644
index 00000000..66212906
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValWeirdTypes.java
@@ -0,0 +1,47 @@
+import java.util.*;
+public class ValWeirdTypes<Z> {
+ private final List<Z> fieldList;
+ public void testGenerics() {
+ List<String> list = new ArrayList<String>();
+ list.add("Hello, World!");
+ final java.lang.String shouldBeString = list.get(0);
+ final java.util.List<java.lang.String> shouldBeListOfString = list;
+ final java.util.List<java.lang.String> shouldBeListOfStringToo = Arrays.asList("hello", "world");
+ final java.lang.String shouldBeString2 = shouldBeListOfString.get(0);
+ }
+ public void testGenericsInference() {
+ final java.util.List<java.lang.Object> huh = Collections.emptyList();
+ final java.util.List<java.lang.Number> huh2 = Collections.<Number>emptyList();
+ }
+ public void testPrimitives() {
+ final int x = 10;
+ final long y = 5 + 3L;
+ }
+ public void testAnonymousInnerClass() {
+ final java.lang.Runnable y = new Runnable(){
+ public void run() {
+ }
+ };
+ }
+ public <T extends Number>void testTypeParams(List<T> param) {
+ final T t = param.get(0);
+ final Z z = fieldList.get(0);
+ final java.util.List<T> k = param;
+ final java.util.List<Z> y = fieldList;
+ }
+ public void testBounds(List<? extends Number> lower, List<? super Number> upper) {
+ final java.lang.Number a = lower.get(0);
+ final java.lang.Object b = upper.get(0);
+ final java.util.List<? extends java.lang.Number> c = lower;
+ final java.util.List<? super java.lang.Number> d = upper;
+ List<?> unbound = lower;
+ final java.util.List<?> e = unbound;
+ }
+ public void testCompound() {
+ final java.util.ArrayList<java.lang.String> a = new ArrayList<String>();
+ final java.util.Vector<java.lang.String> b = new Vector<String>();
+ final boolean c = 1 < System.currentTimeMillis();
+ final java.util.AbstractList<java.lang.String> d = c ? a : b;
+ java.util.RandomAccess confirm = c ? a : b;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValComplex.java b/test/transform/resource/before/ValComplex.java
new file mode 100644
index 00000000..5f718003
--- /dev/null
+++ b/test/transform/resource/before/ValComplex.java
@@ -0,0 +1,20 @@
+public class ValComplex {
+ private ValSimple field = new ValSimple();
+ private static final int CONSTANT = 20;
+
+ public void testReferencingOtherFiles() {
+ val shouldBeString = field.method();
+ val shouldBeInt = CONSTANT;
+ val lock = new Object();
+ synchronized (lock) {
+ val field = 20; //Shadowing
+ val inner = 10;
+ switch (field) {
+ case 5:
+ val shouldBeString2 = shouldBeString;
+ val innerInner = inner;
+ }
+ }
+ val shouldBeValSimple = field; //Unshadowing
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValErrors.java b/test/transform/resource/before/ValErrors.java
new file mode 100644
index 00000000..742bca6d
--- /dev/null
+++ b/test/transform/resource/before/ValErrors.java
@@ -0,0 +1,9 @@
+public class ValErrors {
+ public void nullType() {
+ val a = null;
+ }
+
+ public void unresolvableExpression() {
+ val c = d;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValInFor.java b/test/transform/resource/before/ValInFor.java
new file mode 100644
index 00000000..af13540e
--- /dev/null
+++ b/test/transform/resource/before/ValInFor.java
@@ -0,0 +1,20 @@
+public class ValInFor {
+ {
+ val x = 10;
+ val x2 = -1;
+ val a = "Hello";
+ for (val y = x, z = x2; y < 20; y++) {
+ val q = y;
+ val w = z;
+ val v = a;
+ }
+ }
+
+/* public void enhancedFor() {
+ java.util.List<String> list = java.util.Arrays.asList("Hello, World!");
+ for (val shouldBeString : list) {
+ System.out.println(shouldBeString.toLowerCase());
+ val shouldBeString2 = shouldBeString;
+ }
+ }*/
+} \ No newline at end of file
diff --git a/test/transform/resource/before/ValLessSimple.java b/test/transform/resource/before/ValLessSimple.java
new file mode 100644
index 00000000..bae7b73b
--- /dev/null
+++ b/test/transform/resource/before/ValLessSimple.java
@@ -0,0 +1,31 @@
+public class ValLessSimple {
+ private short field2 = 5;
+
+ private String method() {
+ return "method";
+ }
+
+ private double method2() {
+ return 2.0;
+ }
+
+ {
+ System.out.println("Hello");
+ val z = 20;
+ val x = 10;
+ val a = z;
+ val y = field2;
+ }
+
+ private void testVal(String param) {
+ val fieldV = field;
+ val a = 10;
+ val b = 20;
+ {
+ val methodV = method();
+ val foo = fieldV + methodV;
+ }
+ }
+
+ private String field = "field";
+}
diff --git a/test/transform/resource/before/ValSimple.java b/test/transform/resource/before/ValSimple.java
new file mode 100644
index 00000000..15508bbc
--- /dev/null
+++ b/test/transform/resource/before/ValSimple.java
@@ -0,0 +1,26 @@
+public class ValSimple {
+ private String field = "field";
+ private short field2 = 5;
+
+ private String method() {
+ return "method";
+ }
+
+ private double method2() {
+ return 2.0;
+ }
+
+ private void testVal(String param) {
+ val fieldV = field;
+ val methodV = method();
+ val paramV = param;
+
+ val valOfVal = fieldV;
+ val operatorV = fieldV + valOfVal;
+
+ val fieldW = field2;
+ val methodW = method2();
+ byte localVar = 3;
+ val operatorW = fieldW + localVar;
+ }
+}
diff --git a/test/transform/resource/before/ValWeirdTypes.java b/test/transform/resource/before/ValWeirdTypes.java
new file mode 100644
index 00000000..6f6eb9db
--- /dev/null
+++ b/test/transform/resource/before/ValWeirdTypes.java
@@ -0,0 +1,54 @@
+import java.util.*;
+
+public class ValWeirdTypes<Z> {
+ private final List<Z> fieldList;
+
+ public void testGenerics() {
+ List<String> list = new ArrayList<String>();
+ list.add("Hello, World!");
+ val shouldBeString = list.get(0);
+ val shouldBeListOfString = list;
+ val shouldBeListOfStringToo = Arrays.asList("hello", "world");
+ val shouldBeString2 = shouldBeListOfString.get(0);
+ }
+
+ public void testGenericsInference() {
+ val huh = Collections.emptyList();
+ val huh2 = Collections.<Number>emptyList();
+ }
+
+ public void testPrimitives() {
+ val x = 10;
+ val y = 5 + 3L;
+ }
+
+ public void testAnonymousInnerClass() {
+ val y = new Runnable() {
+ public void run() {}
+ };
+ }
+
+ public <T extends Number> void testTypeParams(List<T> param) {
+ val t = param.get(0);
+ val z = fieldList.get(0);
+ val k = param;
+ val y = fieldList;
+ }
+
+ public void testBounds(List<? extends Number> lower, List<? super Number> upper) {
+ val a = lower.get(0);
+ val b = upper.get(0);
+ val c = lower;
+ val d = upper;
+ List<?> unbound = lower;
+ val e = unbound;
+ }
+
+ public void testCompound() {
+ val a = new ArrayList<String>();
+ val b = new Vector<String>();
+ val c = 1 < System.currentTimeMillis();
+ val d = c ? a : b;
+ java.util.RandomAccess confirm = c ? a : b;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/messages-delombok/ValErrors.java.messages b/test/transform/resource/messages-delombok/ValErrors.java.messages
new file mode 100644
index 00000000..feba6912
--- /dev/null
+++ b/test/transform/resource/messages-delombok/ValErrors.java.messages
@@ -0,0 +1,2 @@
+3:21 ERROR Cannot use 'val' here because initializer expression does not have a representable type: <nulltype>
+7:21 ERROR Cannot use 'val' here because initializer expression does not have a representable type: Type cannot be resolved