aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/javac
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2013-07-22 23:23:46 +0200
committerReinier Zwitserloot <reinier@zwitserloot.com>2013-07-22 23:23:46 +0200
commit45697b50816df79475a8bb69dc89ff68747fbfe6 (patch)
tree25cb023eec1f74baf5063cc5a58a5351ee43d6f0 /src/core/lombok/javac
parent4c03e3d220900431085897878d4888bf530b31ec (diff)
parentdeed98be16e5099af52d951fc611f86a82a42858 (diff)
downloadlombok-45697b50816df79475a8bb69dc89ff68747fbfe6.tar.gz
lombok-45697b50816df79475a8bb69dc89ff68747fbfe6.tar.bz2
lombok-45697b50816df79475a8bb69dc89ff68747fbfe6.zip
Merge branch 'master' into jdk8. Also added some major fixes whilst merging.
Conflicts: src/core/lombok/javac/handlers/JavacHandlerUtil.java src/utils/lombok/javac/CommentCatcher.java src/utils/lombok/javac/Javac.java
Diffstat (limited to 'src/core/lombok/javac')
-rw-r--r--src/core/lombok/javac/FindTypeVarScanner.java2
-rw-r--r--src/core/lombok/javac/HandlerLibrary.java32
-rw-r--r--src/core/lombok/javac/Javac6BasedLombokOptions.java3
-rw-r--r--src/core/lombok/javac/JavacAST.java89
-rw-r--r--src/core/lombok/javac/JavacAnnotationHandler.java2
-rw-r--r--src/core/lombok/javac/JavacImportList.java112
-rw-r--r--src/core/lombok/javac/JavacResolution.java4
-rw-r--r--src/core/lombok/javac/LombokOptions.java4
-rw-r--r--src/core/lombok/javac/apt/Processor.java4
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java330
-rw-r--r--src/core/lombok/javac/handlers/HandleCleanup.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java38
-rw-r--r--src/core/lombok/javac/handlers/HandleData.java3
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java22
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldDefaults.java10
-rw-r--r--src/core/lombok/javac/handlers/HandleGetter.java73
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java17
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java42
-rw-r--r--src/core/lombok/javac/handlers/HandleSneakyThrows.java29
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java26
-rw-r--r--src/core/lombok/javac/handlers/HandleValue.java12
-rw-r--r--src/core/lombok/javac/handlers/HandleWither.java5
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java217
-rw-r--r--src/core/lombok/javac/handlers/NonNullHandler.java148
24 files changed, 1069 insertions, 156 deletions
diff --git a/src/core/lombok/javac/FindTypeVarScanner.java b/src/core/lombok/javac/FindTypeVarScanner.java
index b1b8e525..7c7d9d50 100644
--- a/src/core/lombok/javac/FindTypeVarScanner.java
+++ b/src/core/lombok/javac/FindTypeVarScanner.java
@@ -88,7 +88,7 @@ public class FindTypeVarScanner extends AbstractTypeVisitor6<Void, Void> {
@Override public Void visitTypeVariable(TypeVariable t, Void p) {
Name name = null;
try {
- name = ((Type)t).tsym.name;
+ name = ((Type) t).tsym.name;
} catch (NullPointerException e) {}
if (name != null) typeVariables.add(name.toString());
subVisit(t.getLowerBound());
diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java
index 2be84355..4306b5f2 100644
--- a/src/core/lombok/javac/HandlerLibrary.java
+++ b/src/core/lombok/javac/HandlerLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -231,23 +231,23 @@ public class HandlerLibrary {
* @param annotation 'node.get()' - convenience parameter.
*/
public void handleAnnotation(JCCompilationUnit unit, JavacNode node, JCAnnotation annotation, long priority) {
- TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
+ TypeResolver resolver = new TypeResolver(node.getImportList());
String rawType = annotation.annotationType.toString();
- for (String fqn : resolver.findTypeMatches(node, typeLibrary, rawType)) {
- AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
- if (container == null) continue;
-
- try {
- if (container.getPriority() == priority) {
- if (checkAndSetHandled(annotation)) container.handle(node);
- }
- } catch (AnnotationValueDecodeFail fail) {
- fail.owner.setError(fail.getMessage(), fail.idx);
- } catch (Throwable t) {
- String sourceName = "(unknown).java";
- if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName();
- javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t);
+ String fqn = resolver.typeRefToFullyQualifiedName(node, typeLibrary, rawType);
+ if (fqn == null) return;
+ AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
+ if (container == null) return;
+
+ try {
+ if (container.getPriority() == priority) {
+ if (checkAndSetHandled(annotation)) container.handle(node);
}
+ } catch (AnnotationValueDecodeFail fail) {
+ fail.owner.setError(fail.getMessage(), fail.idx);
+ } catch (Throwable t) {
+ String sourceName = "(unknown).java";
+ if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName();
+ javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t);
}
}
diff --git a/src/core/lombok/javac/Javac6BasedLombokOptions.java b/src/core/lombok/javac/Javac6BasedLombokOptions.java
index 4bb2bdc3..871e41c4 100644
--- a/src/core/lombok/javac/Javac6BasedLombokOptions.java
+++ b/src/core/lombok/javac/Javac6BasedLombokOptions.java
@@ -26,7 +26,6 @@ import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Options;
public class Javac6BasedLombokOptions extends LombokOptions {
-
public static Javac6BasedLombokOptions replaceWithDelombokOptions(Context context) {
Options options = Options.instance(context);
context.put(optionsKey, (Options)null);
@@ -38,7 +37,7 @@ public class Javac6BasedLombokOptions extends LombokOptions {
private Javac6BasedLombokOptions(Context context) {
super(context);
}
-
+
@Override public void putJavacOption(String optionName, String value) {
put(OptionName.valueOf(optionName), value);
}
diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java
index 71c17538..36c51210 100644
--- a/src/core/lombok/javac/JavacAST.java
+++ b/src/core/lombok/javac/JavacAST.java
@@ -24,6 +24,7 @@ package lombok.javac;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Messager;
@@ -34,10 +35,12 @@ import javax.tools.JavaFileObject;
import lombok.core.AST;
import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCatch;
+import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -46,7 +49,6 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
-import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
@@ -78,7 +80,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
* @param top The compilation unit, which serves as the top level node in the tree to be built.
*/
public JavacAST(Messager messager, Context context, JCCompilationUnit top) {
- super(sourceName(top), packageDeclaration(top), imports(top));
+ super(sourceName(top), packageDeclaration(top), new JavacImportList(top));
setTop(buildCompilationUnit(top));
this.context = context;
this.messager = messager;
@@ -98,16 +100,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return (cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent) ? cu.pid.toString() : null;
}
- private static Collection<String> imports(JCCompilationUnit cu) {
- List<String> imports = new ArrayList<String>();
- for (JCTree def : cu.defs) {
- if (def instanceof JCImport) {
- imports.add(((JCImport)def).qualid.toString());
- }
- }
- return imports;
- }
-
public Context getContext() {
return context;
}
@@ -121,9 +113,20 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
void traverseChildren(JavacASTVisitor visitor, JavacNode node) {
- for (JavacNode child : new ArrayList<JavacNode>(node.down())) {
- child.traverse(visitor);
- }
+ for (JavacNode child : node.down()) child.traverse(visitor);
+ }
+
+ @Override public int getSourceVersion() {
+ try {
+ String nm = Source.instance(context).name();
+ int underscoreIdx = nm.indexOf('_');
+ if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1));
+ } catch (Exception ignore) {}
+ return 6;
+ }
+
+ @Override public int getLatestJavaSpecSupported() {
+ return Javac.getJavaCompilerVersion();
}
/** @return A Name object generated for the proper name table belonging to this AST. */
@@ -223,6 +226,46 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return putInMap(new JavacNode(this, local, childNodes, kind));
}
+ private static boolean JCTRY_RESOURCES_FIELD_INITIALIZED;
+ private static Field JCTRY_RESOURCES_FIELD;
+
+ @SuppressWarnings("unchecked")
+ private static List<JCTree> getResourcesForTryNode(JCTry tryNode) {
+ if (!JCTRY_RESOURCES_FIELD_INITIALIZED) {
+ try {
+ JCTRY_RESOURCES_FIELD = JCTry.class.getField("resources");
+ } catch (NoSuchFieldException ignore) {
+ // Java 1.6 or lower won't have this at all.
+ } catch (Exception ignore) {
+ // Shouldn't happen. Best thing we can do is just carry on and break on try/catch.
+ }
+ JCTRY_RESOURCES_FIELD_INITIALIZED = true;
+ }
+
+ if (JCTRY_RESOURCES_FIELD == null) return Collections.emptyList();
+ Object rv = null;
+ try {
+ rv = JCTRY_RESOURCES_FIELD.get(tryNode);
+ } catch (Exception ignore) {}
+
+ if (rv instanceof List) return (List<JCTree>) rv;
+ return Collections.emptyList();
+ }
+
+ private JavacNode buildTry(JCTry tryNode) {
+ if (setAndGetAsHandled(tryNode)) return null;
+ List<JavacNode> childNodes = new ArrayList<JavacNode>();
+ for (JCTree varDecl : getResourcesForTryNode(tryNode)) {
+ if (varDecl instanceof JCVariableDecl) {
+ addIfNotNull(childNodes, buildLocalVar((JCVariableDecl) varDecl, Kind.LOCAL));
+ }
+ }
+ addIfNotNull(childNodes, buildStatement(tryNode.body));
+ for (JCCatch jcc : tryNode.catchers) addIfNotNull(childNodes, buildTree(jcc, Kind.STATEMENT));
+ addIfNotNull(childNodes, buildStatement(tryNode.finalizer));
+ return putInMap(new JavacNode(this, tryNode, childNodes, Kind.STATEMENT));
+ }
+
private JavacNode buildInitializer(JCBlock initializer) {
if (setAndGetAsHandled(initializer)) return null;
List<JavacNode> childNodes = new ArrayList<JavacNode>();
@@ -264,6 +307,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
if (statement instanceof JCAnnotation) return null;
if (statement instanceof JCClassDecl) return buildType((JCClassDecl)statement);
if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl)statement, Kind.LOCAL);
+ if (statement instanceof JCTry) return buildTry((JCTry) statement);
if (setAndGetAsHandled(statement)) return null;
@@ -271,9 +315,18 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
private JavacNode drill(JCTree statement) {
- List<JavacNode> childNodes = new ArrayList<JavacNode>();
- for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
- return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
+ try {
+ List<JavacNode> childNodes = new ArrayList<JavacNode>();
+ for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
+ return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
+ } catch (OutOfMemoryError oome) {
+ String msg = oome.getMessage();
+ if (msg == null) msg = "(no original message)";
+ OutOfMemoryError newError = new OutOfMemoryError(getFileName() + "@pos" + statement.getPreferredPosition() + ": " + msg);
+ // We could try to set the stack trace of the new exception to the same one as the old exception, but this costs memory,
+ // and we're already in an extremely fragile situation in regards to remaining heap space, so let's not do that.
+ throw newError;
+ }
}
/** For javac, both JCExpression and JCStatement are considered as valid children types. */
diff --git a/src/core/lombok/javac/JavacAnnotationHandler.java b/src/core/lombok/javac/JavacAnnotationHandler.java
index 169e2026..a86aa6c6 100644
--- a/src/core/lombok/javac/JavacAnnotationHandler.java
+++ b/src/core/lombok/javac/JavacAnnotationHandler.java
@@ -33,7 +33,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation;
*
* You MUST replace 'T' with a specific annotation type, such as:
*
- * {@code public class HandleGetter implements JavacAnnotationHandler<Getter>}
+ * {@code public class HandleGetter extends JavacAnnotationHandler<Getter>}
*
* Because this generics parameter is inspected to figure out which class you're interested in.
*
diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java
new file mode 100644
index 00000000..d5d7460a
--- /dev/null
+++ b/src/core/lombok/javac/JavacImportList.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 The Project Lombok Authors.
+ *
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCImport;
+import com.sun.tools.javac.util.List;
+
+import lombok.core.ImportList;
+import lombok.core.LombokInternalAliasing;
+
+public class JavacImportList implements ImportList {
+ private final JCExpression pkg;
+ private final List<JCTree> defs;
+
+ public JavacImportList(JCCompilationUnit cud) {
+ this.pkg = cud.pid;
+ this.defs = cud.defs;
+ }
+
+ @Override public String getFullyQualifiedNameForSimpleName(String unqualified) {
+ for (JCTree def : defs) {
+ if (!(def instanceof JCImport)) continue;
+ JCTree qual = ((JCImport) def).qualid;
+ if (!(qual instanceof JCFieldAccess)) continue;
+ String simpleName = ((JCFieldAccess) qual).name.toString();
+ if (simpleName.equals(unqualified)) {
+ return LombokInternalAliasing.processAliases(qual.toString());
+ }
+ }
+
+ return null;
+ }
+
+ @Override public boolean hasStarImport(String packageName) {
+ for (Map.Entry<String, String> e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) {
+ if (e.getValue().equals(packageName) && hasStarImport(e.getKey())) return true;
+ }
+ if (pkg != null && pkg.toString().equals(packageName)) return true;
+ if ("java.lang".equals(packageName)) return true;
+
+ for (JCTree def : defs) {
+ if (!(def instanceof JCImport)) continue;
+ if (((JCImport) def).staticImport) continue;
+ JCTree qual = ((JCImport) def).qualid;
+ if (!(qual instanceof JCFieldAccess)) continue;
+ String simpleName = ((JCFieldAccess) qual).name.toString();
+ if (!"*".equals(simpleName)) continue;
+ if (packageName.equals(((JCFieldAccess) qual).selected.toString())) return true;
+ }
+
+ return false;
+ }
+
+ @Override public Collection<String> applyNameToStarImports(String startsWith, String name) {
+ ArrayList<String> out = new ArrayList<String>();
+
+ if (pkg != null && topLevelName(pkg).equals(startsWith)) out.add(pkg.toString() + "." + name);
+
+ for (JCTree def : defs) {
+ if (!(def instanceof JCImport)) continue;
+ if (((JCImport) def).staticImport) continue;
+ JCTree qual = ((JCImport) def).qualid;
+ if (!(qual instanceof JCFieldAccess)) continue;
+ String simpleName = ((JCFieldAccess) qual).name.toString();
+ if (!"*".equals(simpleName)) continue;
+
+ String topLevelName = topLevelName(qual);
+ if (topLevelName.equals(startsWith)) {
+ out.add(((JCFieldAccess) qual).selected.toString() + "." + name);
+ }
+ }
+
+ return out;
+ }
+
+ private String topLevelName(JCTree tree) {
+ while (tree instanceof JCFieldAccess) tree = ((JCFieldAccess) tree).selected;
+ return tree.toString();
+ }
+
+ @Override public String applyUnqualifiedNameToPackage(String unqualified) {
+ if (pkg == null) return unqualified;
+ return pkg.toString() + "." + unqualified;
+ }
+}
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
index e5d8ed38..82ce0cb8 100644
--- a/src/core/lombok/javac/JavacResolution.java
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -433,8 +433,8 @@ public class JavacResolution {
if (symbol.name.length() == 0) {
// Anonymous inner class
if (type instanceof ClassType) {
- List<Type> ifaces = ((ClassType)type).interfaces_field;
- Type supertype = ((ClassType)type).supertype_field;
+ List<Type> ifaces = ((ClassType) type).interfaces_field;
+ Type supertype = ((ClassType) type).supertype_field;
if (ifaces != null && ifaces.length() == 1) {
return typeToJCTree(ifaces.get(0), ast, allowCompound, allowVoid);
}
diff --git a/src/core/lombok/javac/LombokOptions.java b/src/core/lombok/javac/LombokOptions.java
index 66e42fe7..f1567e9d 100644
--- a/src/core/lombok/javac/LombokOptions.java
+++ b/src/core/lombok/javac/LombokOptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Project Lombok Authors.
+ * Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -49,6 +49,6 @@ public abstract class LombokOptions extends Options {
protected LombokOptions(Context context) {
super(context);
}
-
+
public abstract void putJavacOption(String optionName, String value);
}
diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java
index 96150b06..110acaad 100644
--- a/src/core/lombok/javac/apt/Processor.java
+++ b/src/core/lombok/javac/apt/Processor.java
@@ -62,10 +62,6 @@ import com.sun.tools.javac.util.Context;
/**
* This Annotation Processor is the standard injection mechanism for lombok-enabling the javac compiler.
*
- * Due to lots of changes in the core javac code, as well as lombok's heavy usage of non-public API, this
- * code only works for the javac v1.6 compiler; it definitely won't work for javac v1.5, and it probably
- * won't work for javac v1.7 without modifications.
- *
* To actually enable lombok in a javac compilation run, this class should be in the classpath when
* running javac; that's the only requirement.
*/
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
new file mode 100644
index 00000000..e60819da
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2013 The Project Lombok Authors.
+ *
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeApply;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.AccessLevel;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.core.TransformationsUtil;
+import lombok.experimental.Builder;
+import lombok.experimental.NonFinal;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
+import static lombok.javac.Javac.*;
+import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+@ProviderFor(JavacAnnotationHandler.class)
+@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
+public class HandleBuilder extends JavacAnnotationHandler<Builder> {
+ @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ Builder builderInstance = annotation.getInstance();
+ String builderMethodName = builderInstance.builderMethodName();
+ String buildMethodName = builderInstance.buildMethodName();
+ String builderClassName = builderInstance.builderClassName();
+
+ if (builderMethodName == null) builderMethodName = "builder";
+ if (buildMethodName == null) buildMethodName = "build";
+ if (builderClassName == null) builderClassName = "";
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+ if (!builderClassName.isEmpty()) {
+ if (!checkName("builderClassName", builderClassName, annotationNode)) return;
+ }
+
+ deleteAnnotationIfNeccessary(annotationNode, Builder.class);
+ deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");
+
+ JavacNode parent = annotationNode.up();
+
+ java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
+ java.util.List<Name> namesOfParameters = new ArrayList<Name>();
+ JCExpression returnType;
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrownExceptions = List.nil();
+ Name nameOfStaticBuilderMethod;
+ JavacNode tdParent;
+
+ JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null;
+
+ if (parent.get() instanceof JCClassDecl) {
+ tdParent = parent;
+ JCClassDecl td = (JCClassDecl) tdParent.get();
+ ListBuffer<JavacNode> allFields = ListBuffer.lb();
+ @SuppressWarnings("deprecation")
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
+ for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
+ // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
+ // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
+ // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
+ if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
+ namesOfParameters.add(fd.name);
+ typesOfParameters.add(fd.vartype);
+ allFields.append(fieldNode);
+ }
+
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, true, annotationNode);
+
+ returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
+ typeParams = td.typarams;
+ thrownExceptions = List.nil();
+ nameOfStaticBuilderMethod = null;
+ if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
+ } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
+ if (!fillParametersFrom.typarams.isEmpty()) {
+ annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
+ return;
+ }
+ tdParent = parent.up();
+ JCClassDecl td = (JCClassDecl) tdParent.get();
+ returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
+ typeParams = td.typarams;
+ thrownExceptions = fillParametersFrom.thrown;
+ nameOfStaticBuilderMethod = null;
+ if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
+ } else if (fillParametersFrom != null) {
+ tdParent = parent.up();
+ JCClassDecl td = (JCClassDecl) tdParent.get();
+ if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) {
+ annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ return;
+ }
+ returnType = fillParametersFrom.restype;
+ typeParams = fillParametersFrom.typarams;
+ thrownExceptions = fillParametersFrom.thrown;
+ nameOfStaticBuilderMethod = fillParametersFrom.name;
+ if (builderClassName.isEmpty()) {
+ if (returnType instanceof JCTypeApply) {
+ returnType = ((JCTypeApply) returnType).clazz;
+ }
+ if (returnType instanceof JCFieldAccess) {
+ builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
+ } else if (returnType instanceof JCIdent) {
+ Name n = ((JCIdent) returnType).name;
+
+ for (JCTypeParameter tp : typeParams) {
+ if (tp.name.equals(n)) {
+ annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
+ return;
+ }
+ }
+ builderClassName = n.toString() + "Builder";
+ } else if (returnType instanceof JCPrimitiveTypeTree) {
+ builderClassName = returnType.toString() + "Builder";
+ if (Character.isLowerCase(builderClassName.charAt(0))) {
+ builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
+ }
+
+ } else {
+ // This shouldn't happen.
+ System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
+ builderClassName = td.name.toString() + "Builder";
+ }
+ }
+ } else {
+ annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ return;
+ }
+
+ if (fillParametersFrom != null) {
+ for (JCVariableDecl param : fillParametersFrom.params) {
+ namesOfParameters.add(param.name);
+ typesOfParameters.add(param.vartype);
+ }
+ }
+
+ JavacNode builderType = findInnerClass(tdParent, builderClassName);
+ if (builderType == null) {
+ builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ } else {
+ sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
+ }
+ java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
+ java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
+ for (JavacNode fieldNode : fieldNodes) {
+ JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast, builderInstance.fluent(), builderInstance.chain());
+ if (newMethod != null) newMethods.add(newMethod);
+ }
+
+ if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), true, ast);
+ if (cd != null) injectMethod(builderType, cd);
+ }
+
+ for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);
+
+ if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions);
+ if (md != null) injectMethod(builderType, md);
+ }
+
+ if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
+ if (md != null) injectMethod(builderType, md);
+ }
+
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
+ TreeMaker maker = type.getTreeMaker();
+
+ JCExpression call;
+ JCStatement statement;
+
+ ListBuffer<JCExpression> args = ListBuffer.lb();
+ for (Name n : fieldNames) {
+ args.append(maker.Ident(n));
+ }
+
+ if (staticName == null) {
+ call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
+ statement = maker.Return(call);
+ } else {
+ ListBuffer<JCExpression> typeParams = ListBuffer.lb();
+ for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
+ typeParams.append(maker.Ident(tp.name));
+ }
+