aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/javac
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lombok/javac')
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java198
-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/HandleValue.java3
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java19
5 files changed, 243 insertions, 18 deletions
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
new file mode 100644
index 00000000..c39255f2
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -0,0 +1,198 @@
+/*
+ * 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 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.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.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.Name;
+
+import lombok.AccessLevel;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.experimental.Builder;
+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.*;
+
+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;
+ }
+
+ 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();
+ new HandleConstructor().generateAllArgsConstructor(tdParent, AccessLevel.PRIVATE, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode);
+
+ for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
+ namesOfParameters.add(fd.name);
+ typesOfParameters.add(fd.vartype);
+ }
+
+ returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
+ typeParams = td.typarams;
+ thrownExceptions = null;
+ 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();
+ } 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();
+ } else if (returnType instanceof JCIdent) {
+ Name n = ((JCIdent) returnType).name;
+
+ for (JCTypeParameter tp : typeParams) {
+ if (tp.name.contentEquals(n)) {
+ annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
+ return;
+ }
+ }
+ builderClassName = n.toString();
+ } 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();
+ }
+ }
+ } 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);
+ java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
+ java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
+ for (JavacNode fieldNode : fieldNodes) {
+ JCMethodDecl newMethod = makeSetterMethodForBuider(builderType, fieldNode, ast);
+ 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, ast, thrownExceptions);
+ if (md != null) injectMethod(builderType, md);
+ }
+
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ private JavacNode findInnerClass(JavacNode parent, String name) {
+ for (JavacNode child : parent.down()) {
+ if (child.getKind() != Kind.TYPE) continue;
+ JCClassDecl td = (JCClassDecl) child.get();
+ if (td.name.contentEquals(name)) return child;
+ }
+ return null;
+ }
+
+ private JavacNode makeBuilderClass(JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
+ TreeMaker maker = tdParent.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC);
+ JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), typeParams, null, List.<JCExpression>nil(), List.<JCTree>nil());
+ return injectType(tdParent, builder);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java
index bb883ca4..ecd982e9 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.java
@@ -29,6 +29,7 @@ import lombok.RequiredArgsConstructor;
import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.core.AST.Kind;
+import lombok.experimental.Builder;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
@@ -66,7 +67,7 @@ public class HandleConstructor {
String staticName = ann.staticName();
if (level == AccessLevel.NONE) return;
List<JavacNode> fields = List.nil();
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, false, false, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, SkipIfConstructorExists.NO, false, annotationNode);
}
}
@@ -84,7 +85,7 @@ public class HandleConstructor {
@SuppressWarnings("deprecation")
boolean suppressConstructorProperties = ann.suppressConstructorProperties();
if (level == AccessLevel.NONE) return;
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, false, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
@@ -119,11 +120,11 @@ public class HandleConstructor {
@SuppressWarnings("deprecation")
boolean suppressConstructorProperties = ann.suppressConstructorProperties();
if (level == AccessLevel.NONE) return;
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, false, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
- private static List<JavacNode> findAllFields(JavacNode typeNode) {
+ static List<JavacNode> findAllFields(JavacNode typeNode) {
ListBuffer<JavacNode> fields = ListBuffer.lb();
for (JavacNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -154,25 +155,34 @@ public class HandleConstructor {
return true;
}
- public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, JavacNode source) {
+ public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, source);
}
- public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, JavacNode source) {
+ public enum SkipIfConstructorExists {
+ YES, NO, I_AM_BUILDER;
+ }
+
+ public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), staticName, skipIfConstructorExists, false, source);
}
- public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties, JavacNode source) {
+ public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, boolean suppressConstructorProperties, JavacNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
- if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
- if (skipIfConstructorExists) {
+ if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
+ if (skipIfConstructorExists != SkipIfConstructorExists.NO) {
for (JavacNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
- if (annotationTypeMatches(NoArgsConstructor.class, child) ||
+ boolean skipGeneration = annotationTypeMatches(NoArgsConstructor.class, child) ||
annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child)) {
-
+ annotationTypeMatches(RequiredArgsConstructor.class, child);
+
+ if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
+ skipGeneration = annotationTypeMatches(Builder.class, child);
+ }
+
+ if (skipGeneration) {
if (staticConstrRequired) {
// @Data has asked us to generate a constructor, but we're going to skip this instruction, as an explicit 'make a constructor' annotation
// will take care of it. However, @Data also wants a specific static name; this will be ignored; the appropriate way to do this is to use
@@ -207,7 +217,7 @@ public class HandleConstructor {
mods.annotations = mods.annotations.append(annotation);
}
- private JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean suppressConstructorProperties, JCTree source) {
+ static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean suppressConstructorProperties, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
@@ -243,7 +253,7 @@ public class HandleConstructor {
null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source);
}
- private boolean isLocalType(JavacNode type) {
+ private static boolean isLocalType(JavacNode type) {
Kind kind = type.up().getKind();
if (kind == Kind.COMPILATION_UNIT) return false;
if (kind == Kind.TYPE) return isLocalType(type.up());
diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java
index 62183a15..858fb543 100644
--- a/src/core/lombok/javac/handlers/HandleData.java
+++ b/src/core/lombok/javac/handlers/HandleData.java
@@ -27,6 +27,7 @@ import lombok.Data;
import lombok.core.AnnotationValues;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import org.mangosdk.spi.ProviderFor;
@@ -50,7 +51,7 @@ public class HandleData extends JavacAnnotationHandler<Data> {
String staticConstructorName = annotation.getInstance().staticConstructor();
// TODO move this to the end OR move it to the top in eclipse.
- new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode);
+ new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java
index f5b10bc1..a59865f7 100644
--- a/src/core/lombok/javac/handlers/HandleValue.java
+++ b/src/core/lombok/javac/handlers/HandleValue.java
@@ -29,6 +29,7 @@ import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import org.mangosdk.spi.ProviderFor;
@@ -65,7 +66,7 @@ public class HandleValue extends JavacAnnotationHandler<Value> {
new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
// TODO move this to the end OR move it to the top in eclipse.
- new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode);
+ new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 2577befb..23bc2daf 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -806,12 +806,13 @@ public class JavacHandlerUtil {
*
* @param typeNode parent type to inject new type into
* @param type New type (class, interface, etc) to inject.
+ * @return
*/
- public static void injectType(final JavacNode typeNode, final JCClassDecl type) {
+ public static JavacNode injectType(final JavacNode typeNode, final JCClassDecl type) {
JCClassDecl typeDecl = (JCClassDecl) typeNode.get();
addSuppressWarningsAll(type.mods, typeNode, type.pos, getGeneratedBy(type));
typeDecl.defs = typeDecl.defs.append(type);
- typeNode.add(type, Kind.TYPE);
+ return typeNode.add(type, Kind.TYPE);
}
private static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source) {
@@ -1025,6 +1026,20 @@ public class JavacHandlerUtil {
return result.toList();
}
+ public static JCExpression namePlusTypeParamsToTypeReference(TreeMaker maker, Name typeName, List<JCTypeParameter> params) {
+ ListBuffer<JCExpression> typeArgs = ListBuffer.lb();
+
+ if (!params.isEmpty()) {
+ for (JCTypeParameter param : params) {
+ typeArgs.append(maker.Ident(param.name));
+ }
+
+ return maker.TypeApply(maker.Ident(typeName), typeArgs.toList());
+ }
+
+ return maker.Ident(typeName);
+ }
+
static List<JCAnnotation> copyAnnotations(List<? extends JCExpression> in) {
ListBuffer<JCAnnotation> out = ListBuffer.lb();
for (JCExpression expr : in) {