From 0ffbd3eec7079b598e3364b90dc949357ad09654 Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Mon, 2 Apr 2018 19:39:45 +0200 Subject: generics for the Builder class; generate abstract methods --- src/core/lombok/javac/handlers/HandleSetter.java | 43 ++++++------ .../lombok/javac/handlers/HandleSuperBuilder.java | 79 +++++++++++++++++----- 2 files changed, 86 insertions(+), 36 deletions(-) diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 1453aa27..77919b8e 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -21,23 +21,14 @@ */ package lombok.javac.handlers; -import static lombok.javac.Javac.*; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; +import static lombok.javac.handlers.JavacHandlerUtil.toAllSetterNames; +import static lombok.javac.handlers.JavacHandlerUtil.toSetterName; import java.util.Collection; -import lombok.AccessLevel; -import lombok.ConfigurationKeys; -import lombok.Setter; -import lombok.core.AST.Kind; -import lombok.core.AnnotationValues; -import lombok.javac.Javac; -import lombok.javac.JavacAnnotationHandler; -import lombok.javac.JavacNode; -import lombok.javac.JavacTreeMaker; -import lombok.javac.handlers.JavacHandlerUtil.FieldAccess; - import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; @@ -57,6 +48,18 @@ 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.ConfigurationKeys; +import lombok.Setter; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.javac.Javac; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc; +import lombok.javac.handlers.JavacHandlerUtil.FieldAccess; + /** * Handles the {@code lombok.Setter} annotation for javac. */ @@ -210,6 +213,12 @@ public class HandleSetter extends JavacAnnotationHandler { } public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List onMethod, List onParam) { + JCExpression returnType = cloneSelfType(field); + JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); + return createSetter(access, deprecate, field, treeMaker, setterName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam); + } + + public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, JCExpression methodType, JCReturn returnStatement, JavacNode source, List onMethod, List onParam) { if (setterName == null) return null; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); @@ -240,19 +249,13 @@ public class HandleSetter extends JavacAnnotationHandler { statements.append(treeMaker.Exec(setBool)); } - JCExpression methodType = null; - 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(Javac.createVoidType(field.getSymbolTable(), CTC_VOID)); - shouldReturnThis = false; + returnStatement = null; } - if (shouldReturnThis) { - JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this"))); + if (returnStatement != null) { statements.append(returnStatement); } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 74b24bdb..94926e80 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -36,9 +36,11 @@ 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.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; +import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; @@ -188,15 +190,15 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create the abstract builder class. JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(true, true, annotationNode, tdParent, builderClassName, useInheritanceOnBuilder ? superclassBuilderClassName : null, typeParams, ast); + builderType = makeBuilderClass(annotationNode, tdParent, builderClassName, useInheritanceOnBuilder ? superclassBuilderClassName : null, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); } - + // Create the builder implementation class. JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { - builderImplType = makeBuilderClass(false, false, annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + builderImplType = makeBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); } else { annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); } @@ -232,7 +234,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { // Create a simple constructor for the builder class. JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.nil(), builderImplType, List.nil(), false, annotationNode); if (cd != null) injectMethod(builderImplType, cd); - + + // Generate abstract getInstance() and build() methods. + injectMethod(builderType, generateAbstractGetInstanceMethod(tdParent)); + injectMethod(builderType, generateAbstractBuildMethod(tdParent, builderMethodName)); + // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { makeSetterMethodsForBuilder(builderType, bfd, annotationNode); @@ -420,6 +426,24 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.nil(), List.nil(), List.nil(), body, null); } + private JCMethodDecl generateAbstractGetInstanceMethod(JavacNode type) { + JavacTreeMaker maker = type.getTreeMaker(); + JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT); + Name name = type.toName("getInstance"); + JCExpression returnType = maker.Ident(type.toName("B")); + + return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); + } + + private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName) { + JavacTreeMaker maker = type.getTreeMaker(); + JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT); + Name name = type.toName(methodName); + JCExpression returnType = maker.Ident(type.toName("C")); + + return maker.MethodDef(modifiers, name, returnType, List.nil(), List.nil(), List.nil(), null, null); + } + private JCMethodDecl generateCleanMethod(java.util.List builderFields, JavacNode type, JCTree source) { JavacTreeMaker maker = type.getTreeMaker(); ListBuffer statements = new ListBuffer(); @@ -549,7 +573,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { JavacTreeMaker maker = fieldNode.getTreeMaker(); - JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.nil(), List.nil()); + JCExpression returnType = maker.Ident(builderType.toName("B")); + JCReturn returnStatement = maker.Return(maker.Ident(builderType.toName("this"))); + + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.nil(), List.nil()); injectMethod(builderType, newMethod); } @@ -563,22 +590,42 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return null; } - public JavacNode makeBuilderClass(boolean isAbstract, boolean isPublic, JavacNode source, JavacNode tdParent, String builderClassName, String parentBuilderClassName, List typeParams, JCAnnotation ast) { + public JavacNode makeBuilderClass(JavacNode source, JavacNode tdParent, String builderClass, String parentBuilderClass, List typeParams, JCAnnotation ast) { JavacTreeMaker maker = tdParent.getTreeMaker(); - int modifiers = Flags.STATIC; - if (isAbstract) modifiers |= Flags.ABSTRACT; - if (isPublic) { - modifiers |= Flags.PUBLIC; - } else { - modifiers |= Flags.PRIVATE; + JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC); + + JCExpression extending = null; + if (parentBuilderClass != null) { + extending = maker.Ident(tdParent.toName(parentBuilderClass)); } - JCModifiers mods = maker.Modifiers(modifiers); + + // Keep any type params of the annotated class. + // TODO: Prevent name clashes with type params from annotated class. + ListBuffer allTypeParams = new ListBuffer(); + allTypeParams.addAll(copyTypeParams(source, typeParams)); + // Add builder-specific type params required for inheritable builders. + // 1. The return type for the build() method, named "C", which extends the annotated class. + JCIdent annotatedClass = maker.Ident(tdParent.toName(tdParent.getName())); + allTypeParams.add(maker.TypeParameter(tdParent.toName("C"), List.of(annotatedClass))); + // 2. The return type for all setter methods, named "B", which extends this builder class. + Name builderClassName = tdParent.toName(builderClass); + JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), + List.of(maker.Ident(tdParent.toName("C")), maker.Ident(tdParent.toName("B")))); + allTypeParams.add(maker.TypeParameter(tdParent.toName("B"), List.of(typeApply))); + + JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.nil(), List.nil()); + return injectType(tdParent, builder); + } + + public JavacNode makeBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClassName, String builderClassName, List typeParams, JCAnnotation ast) { + JavacTreeMaker maker = tdParent.getTreeMaker(); + JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE); JCExpression extending = null; - if (parentBuilderClassName != null) { - extending = maker.Ident(tdParent.toName(parentBuilderClassName)); + if (builderClassName != null) { + extending = maker.Ident(tdParent.toName(builderClassName)); } - JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); + JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClassName), copyTypeParams(source, typeParams), extending, List.nil(), List.nil()); return injectType(tdParent, builder); } -- cgit