diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2013-06-18 04:23:15 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2013-06-18 04:23:15 +0200 |
commit | e1c39bbc601408decb0ae147d181708a5af41307 (patch) | |
tree | f3a620da1778ae4b9654433bec65841a95ff5067 /src | |
parent | eb3d32c718d9ef46fd30bc677147cda85318fb9c (diff) | |
download | lombok-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')
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) { |