aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2013-06-18 04:23:15 +0200
committerReinier Zwitserloot <reinier@zwitserloot.com>2013-06-18 04:23:15 +0200
commite1c39bbc601408decb0ae147d181708a5af41307 (patch)
treef3a620da1778ae4b9654433bec65841a95ff5067 /src/core
parenteb3d32c718d9ef46fd30bc677147cda85318fb9c (diff)
downloadlombok-e1c39bbc601408decb0ae147d181708a5af41307.tar.gz
lombok-e1c39bbc601408decb0ae147d181708a5af41307.tar.bz2
lombok-e1c39bbc601408decb0ae147d181708a5af41307.zip
javac builder implementation. Passes all tests.
Added toString() impl for builders in both eclipse and javac. Added all documentation, though it'll need some reviewing.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java7
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java8
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java133
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java12
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java6
5 files changed, 145 insertions, 21 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index d15f00e6..e2bf5fe2 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -78,7 +78,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (buildMethodName == null) builderMethodName = "build";
if (builderClassName == null) builderClassName = "";
- if (checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
if (!builderClassName.isEmpty()) {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
@@ -200,6 +200,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (md != null) injectMethod(builderType, md);
}
+ if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
+ if (md != null) injectMethod(builderType, md);
+ }
+
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
if (md != null) injectMethod(tdParent, md);
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index d864153f..1193af31 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.java
@@ -170,7 +170,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
}
- private MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
+ static MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
boolean includeFieldNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
String typeName = getTypeName(type);
char[] suffix = ")".toCharArray();
@@ -282,7 +282,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
return method;
}
- private String getTypeName(EclipseNode type) {
+ private static String getTypeName(EclipseNode type) {
String typeName = getSingleTypeName(type);
EclipseNode upType = type.up();
while (upType.getKind() == Kind.TYPE) {
@@ -292,7 +292,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
return typeName;
}
- private String getSingleTypeName(EclipseNode type) {
+ private static String getSingleTypeName(EclipseNode type) {
TypeDeclaration typeDeclaration = (TypeDeclaration)type.get();
char[] rawTypeName = typeDeclaration.name;
return rawTypeName == null ? "" : new String(rawTypeName);
@@ -301,7 +301,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
private static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"byte", "short", "int", "long", "char", "boolean", "double", "float")));
- private NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
+ private static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
NameReference ref;
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index c39255f2..aa485b26 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -22,21 +22,28 @@
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;
@@ -51,6 +58,7 @@ import static lombok.javac.Javac.*;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
+@ProviderFor(JavacAnnotationHandler.class)
public class HandleBuilder extends JavacAnnotationHandler<Builder> {
@Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
Builder builderInstance = annotation.getInstance();
@@ -68,6 +76,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
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>();
@@ -93,7 +104,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
- thrownExceptions = null;
+ thrownExceptions = List.nil();
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
@@ -107,7 +118,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
typeParams = td.typarams;
thrownExceptions = fillParametersFrom.thrown;
nameOfStaticBuilderMethod = null;
- if (builderClassName.isEmpty()) builderClassName = td.name.toString();
+ if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null) {
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
@@ -124,21 +135,27 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
returnType = ((JCTypeApply) returnType).clazz;
}
if (returnType instanceof JCFieldAccess) {
- builderClassName = ((JCFieldAccess) returnType).name.toString();
+ builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
} else if (returnType instanceof JCIdent) {
Name n = ((JCIdent) returnType).name;
for (JCTypeParameter tp : typeParams) {
- if (tp.name.contentEquals(n)) {
+ 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();
+ 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();
+ builderClassName = td.name.toString() + "Builder";
}
}
} else {
@@ -158,7 +175,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
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);
+ JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast);
if (newMethod != null) newMethods.add(newMethod);
}
@@ -170,16 +187,112 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
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);
+ 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, ast);
+ 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));
+ }
+
+ JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
+ call = maker.Apply(typeParams.toList(), fn, args.toList());
+ if (returnType instanceof JCPrimitiveTypeTree && ((JCPrimitiveTypeTree) returnType).typetag == CTC_VOID) {
+ statement = maker.Exec(call);
+ } else {
+ statement = maker.Return(call);
+ }
+ }
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
+ }
+
+ private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) {
+ TreeMaker maker = type.getTreeMaker();
+
+ ListBuffer<JCExpression> typeArgs = ListBuffer.lb();
+ for (JCTypeParameter typeParam : typeParams) {
+ typeArgs.append(maker.Ident(typeParam.name));
+ }
+
+ JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null);
+ JCStatement statement = maker.Return(call);
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
+ private java.util.List<JavacNode> addFieldsToBuilder(JavacNode builderType, java.util.List<Name> namesOfParameters, java.util.List<JCExpression> typesOfParameters, JCTree source) {
+ int len = namesOfParameters.size();
+ java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
+ for (JavacNode child : builderType.down()) {
+ if (child.getKind() == Kind.FIELD) existing.add(child);
+ }
+
+ java.util.List<JavacNode>out = new ArrayList<JavacNode>();
+
+ top:
+ for (int i = len - 1; i >= 0; i--) {
+ Name name = namesOfParameters.get(i);
+ for (JavacNode exists : existing) {
+ Name n = ((JCVariableDecl) exists.get()).name;
+ if (n.equals(name)) {
+ out.add(exists);
+ continue top;
+ }
+ }
+ TreeMaker maker = builderType.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, name, cloneType(maker, typesOfParameters.get(i), source), null);
+ out.add(injectField(builderType, newField));
+ }
+
+ Collections.reverse(out);
+ return out;
+ }
+
+
+ private JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JCTree source) {
+ Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
+ for (JavacNode child : builderType.down()) {
+ if (child.getKind() != Kind.METHOD) continue;
+ Name existingName = ((JCMethodDecl) child.get()).name;
+ if (existingName.equals(fieldName)) return null;
+ }
+
+ TreeMaker maker = builderType.getTreeMaker();
+ return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, fieldName.toString(), true, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ }
+
private JavacNode findInnerClass(JavacNode parent, String name) {
for (JavacNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
@@ -192,7 +305,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
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());
+ JCClassDecl builder = ClassDef(maker, mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
return injectType(tdParent, builder);
}
}
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index c1e03c35..29728eae 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -194,9 +194,13 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
injectMethod(fieldNode.up(), createdSetter);
}
- private JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ static JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
String setterName = toSetterName(field);
boolean returnThis = shouldReturnThis(field);
+ return createSetter(access, field, treeMaker, setterName, returnThis, source, onMethod, onParam);
+ }
+
+ static JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, String setterName, boolean shouldReturnThis, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (setterName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
@@ -222,17 +226,17 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
}
JCExpression methodType = null;
- if (returnThis) {
+ if (shouldReturnThis) {
methodType = cloneSelfType(field);
}
if (methodType == null) {
//WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6.
methodType = treeMaker.Type(new JCNoType(CTC_VOID));
- returnThis = false;
+ shouldReturnThis = false;
}
- if (returnThis) {
+ if (shouldReturnThis) {
JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this")));
statements.append(returnStatement);
}
diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java
index 5b3c033c..333393da 100644
--- a/src/core/lombok/javac/handlers/HandleToString.java
+++ b/src/core/lombok/javac/handlers/HandleToString.java
@@ -24,6 +24,8 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import static lombok.javac.Javac.*;
+import java.util.Collection;
+
import lombok.ToString;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
@@ -164,7 +166,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
}
}
- private JCMethodDecl createToString(JavacNode typeNode, List<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+ static JCMethodDecl createToString(JavacNode typeNode, Collection<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(chainDots(typeNode, "java", "lang", "Override"), List.<JCExpression>nil());
@@ -241,7 +243,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source);
}
- private String getTypeName(JavacNode typeNode) {
+ private static String getTypeName(JavacNode typeNode) {
String typeName = ((JCClassDecl) typeNode.get()).name.toString();
JavacNode upType = typeNode.up();
while (upType.getKind() == Kind.TYPE) {