From b42ce5325afbe6202d5d5815a9c74dd64a0ce3de Mon Sep 17 00:00:00 2001 From: Jan Rieke Date: Tue, 11 Sep 2018 10:32:50 +0200 Subject: SuperBuilder: generate toBuilder method (javac) --- src/core/lombok/javac/handlers/HandleBuilder.java | 2 +- .../lombok/javac/handlers/HandleSuperBuilder.java | 67 +++++++++++++++++----- 2 files changed, 53 insertions(+), 16 deletions(-) (limited to 'src/core') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 63697691..91cd4abb 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -84,7 +84,7 @@ public class HandleBuilder extends JavacAnnotationHandler { return ((Boolean) expr).booleanValue(); } - private static class BuilderFieldData { + public static class BuilderFieldData { JCExpression type; Name rawName; Name name; diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index b8f572d5..62dc93c1 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -68,6 +68,7 @@ import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; +import lombok.javac.handlers.HandleBuilder.BuilderFieldData; import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult; import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker; import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; @@ -78,21 +79,9 @@ import lombok.javac.handlers.JavacSingularsRecipes.SingularData; @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 HandleSuperBuilder extends JavacAnnotationHandler { private static final String SELF_METHOD = "self"; - - private static class BuilderFieldData { - JCExpression type; - Name rawName; - Name name; - Name nameOfDefaultProvider; - Name nameOfSetFlag; - SingularData singularData; - ObtainVia obtainVia; - JavacNode obtainViaNode; - JavacNode originalFieldNode; - - java.util.List createdFields = new ArrayList(); - } - + private static final String TO_BUILDER_METHOD_NAME = "toBuilder"; + private static final String FILL_VALUES_METHOD_NAME = "$fillValuesFrom"; + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); @@ -109,6 +98,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; + boolean toBuilder = superbuilderAnnotation.toBuilder(); + JavacNode tdParent = annotationNode.up(); java.util.List builderFields = new ArrayList(); @@ -307,6 +298,19 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { if (builderMethod != null) injectMethod(tdParent, builderMethod); } } + + if (toBuilder) { + switch (methodExists(TO_BUILDER_METHOD_NAME, tdParent, 0)) { + case EXISTS_BY_USER: + annotationNode.addWarning("Not generating toBuilder() as it already exists."); + return; + case NOT_EXISTS: + JCMethodDecl md = generateToBuilderMethod(builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); + if (md != null) { + injectMethod(tdParent, md); + } + } + } } /** @@ -495,6 +499,39 @@ public class HandleSuperBuilder extends JavacAnnotationHandler { return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); } + /** + * Generates a toBuilder() method that looks like this: + *
+	 * public ParentBuilder<?, ?> toBuilder() {
+	 *     return new FoobarBuilderImpl().$fillValuesFrom(this);
+	 * }
+	 * 
+ */ + private JCMethodDecl generateToBuilderMethod(String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List typeParams) { + JavacTreeMaker maker = type.getTreeMaker(); + + ListBuffer typeArgs = new ListBuffer(); + for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name)); + + JCExpression newClass = maker.NewClass(null, List.nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.nil(), null); + JCMethodInvocation invokeFillMethod = maker.Apply(List.nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), List.nil()); + JCStatement statement = maker.Return(invokeFillMethod); + + JCBlock body = maker.Block(0, List.of(statement)); + int modifiers = Flags.PUBLIC; + + // Add any type params of the annotated class to the return type. + ListBuffer typeParameterNames = new ListBuffer(); + typeParameterNames.addAll(typeParameterNames(maker, typeParams)); + // Now add the . + JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + typeParameterNames.add(wildcard); + typeParameterNames.add(wildcard); + JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList()); + + return maker.MethodDef(maker.Modifiers(modifiers), type.toName(TO_BUILDER_METHOD_NAME), returnType, copyTypeParams(source, typeParams), List.nil(), List.nil(), body, null); + } + private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List annotations = List.nil(); -- cgit