diff options
Diffstat (limited to 'src/core')
28 files changed, 651 insertions, 240 deletions
diff --git a/src/core/lombok/bytecode/AsmUtil.java b/src/core/lombok/bytecode/AsmUtil.java index e3d33efa..42bf9700 100644 --- a/src/core/lombok/bytecode/AsmUtil.java +++ b/src/core/lombok/bytecode/AsmUtil.java @@ -37,7 +37,7 @@ class AsmUtil { ClassReader reader = new ClassReader(byteCode); ClassWriter writer = new FixedClassWriter(reader, 0); - ClassVisitor visitor = new ClassVisitor(Opcodes.ASM6, writer) { + ClassVisitor visitor = new ClassVisitor(Opcodes.ASM7, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new JSRInlinerAdapter(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc, signature, exceptions); } diff --git a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java index c06f2d7c..20572e22 100644 --- a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java +++ b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java @@ -50,7 +50,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation { class PreventNullAnalysisVisitor extends MethodVisitor { PreventNullAnalysisVisitor(MethodVisitor mv) { - super(Opcodes.ASM6, mv); + super(Opcodes.ASM7, mv); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { @@ -68,7 +68,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation { } } - reader.accept(new ClassVisitor(Opcodes.ASM6, writer) { + reader.accept(new ClassVisitor(Opcodes.ASM7, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new PreventNullAnalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions)); } diff --git a/src/core/lombok/bytecode/SneakyThrowsRemover.java b/src/core/lombok/bytecode/SneakyThrowsRemover.java index a2ee59ea..037cb109 100644 --- a/src/core/lombok/bytecode/SneakyThrowsRemover.java +++ b/src/core/lombok/bytecode/SneakyThrowsRemover.java @@ -52,7 +52,7 @@ public class SneakyThrowsRemover implements PostCompilerTransformation { class SneakyThrowsRemoverVisitor extends MethodVisitor { SneakyThrowsRemoverVisitor(MethodVisitor mv) { - super(Opcodes.ASM6, mv); + super(Opcodes.ASM7, mv); } private boolean methodInsnQueued = false; @@ -177,7 +177,7 @@ public class SneakyThrowsRemover implements PostCompilerTransformation { } } - reader.accept(new ClassVisitor(Opcodes.ASM5, writer) { + reader.accept(new ClassVisitor(Opcodes.ASM7, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new SneakyThrowsRemoverVisitor(super.visitMethod(access, name, desc, signature, exceptions)); } diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java index fe7a4330..afbba1e8 100644 --- a/src/core/lombok/core/AST.java +++ b/src/core/lombok/core/AST.java @@ -39,6 +39,7 @@ import java.util.concurrent.ConcurrentMap; import lombok.core.configuration.ConfigurationKey; import lombok.core.debug.HistogramTracker; +import lombok.permit.Permit; /** * Lombok wraps the AST produced by a target platform into its own AST system, mostly because both Eclipse and javac @@ -252,7 +253,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, } if (shouldDrill(c, t, f.getName())) { - f.setAccessible(true); + Permit.setAccessible(f); fields.add(new FieldAccess(f, dim)); } } diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java index 293bfef6..363952a4 100644 --- a/src/core/lombok/core/AnnotationProcessor.java +++ b/src/core/lombok/core/AnnotationProcessor.java @@ -46,6 +46,7 @@ import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; import lombok.patcher.ClassRootFinder; +import lombok.permit.Permit; @SupportedAnnotationTypes("*") public class AnnotationProcessor extends AbstractProcessor { @@ -82,8 +83,7 @@ public class AnnotationProcessor extends AbstractProcessor { for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) { try { - Field field = procEnvClass.getDeclaredField("delegate"); - field.setAccessible(true); + Field field = Permit.getField(procEnvClass, "delegate"); Object delegate = field.get(procEnv); return tryRecursivelyObtainJavacProcessingEnvironment((ProcessingEnvironment) delegate); @@ -136,7 +136,7 @@ public class AnnotationProcessor extends AbstractProcessor { ClassLoader environmentClassLoader = procEnv.getClass().getClassLoader(); if (environmentClassLoader != null && environmentClassLoader.getClass().getCanonicalName().equals("org.codehaus.plexus.compiler.javac.IsolatedClassLoader")) { if (!ClassLoader_lombokAlreadyAddedTo.getAndSet(environmentClassLoader, true)) { - Method m = environmentClassLoader.getClass().getDeclaredMethod("addURL", URL.class); + Method m = Permit.getMethod(environmentClassLoader.getClass(), "addURL", URL.class); URL selfUrl = new File(ClassRootFinder.findClassRootOfClass(AnnotationProcessor.class)).toURI().toURL(); m.invoke(environmentClassLoader, selfUrl); } diff --git a/src/core/lombok/core/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java index a24330fa..eec5abd8 100644 --- a/src/core/lombok/core/AnnotationValues.java +++ b/src/core/lombok/core/AnnotationValues.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import lombok.core.AST.Kind; +import lombok.permit.Permit; /** * Represents a single annotation in a source file and can be used to query the parameters present on it. @@ -210,7 +211,7 @@ public class AnnotationValues<A extends Annotation> { public <T> T getDefaultIf(String methodName, Class<T> type, T defaultValue) { try { - return type.cast(type.getMethod(methodName).getDefaultValue()); + return type.cast(Permit.getMethod(type, methodName).getDefaultValue()); } catch (Exception e) { return defaultValue; } diff --git a/src/core/lombok/core/Main.java b/src/core/lombok/core/Main.java index 6952ab78..47afaefd 100644 --- a/src/core/lombok/core/Main.java +++ b/src/core/lombok/core/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-2018 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 @@ -41,7 +41,9 @@ public class Main { Thread.currentThread().setContextClassLoader(Main.class.getClassLoader()); int err = new Main(SpiLoadUtil.readAllFromIterator( SpiLoadUtil.findServices(LombokApp.class)), Arrays.asList(args)).go(); - System.exit(err); + if (err != 0) { + System.exit(err); + } } @ProviderFor(LombokApp.class) @@ -143,7 +145,7 @@ public class Main { out.println("------------------------------"); } out.println("projectlombok.org " + Version.getFullVersion()); - out.println("Copyright (C) 2009-2015 The Project Lombok Authors."); + out.println("Copyright (C) 2009-2018 The Project Lombok Authors."); out.println("Run 'lombok license' to see the lombok license agreement."); out.println(); out.println("Run lombok without any parameters to start the graphical installer."); diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 296b70b4..ac91447d 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -85,6 +85,7 @@ public class HandlerUtil { "org.jetbrains.annotations.NotNull", "android.support.annotation.NonNull", "org.eclipse.jdt.annotation.NonNull", + "org.springframework.lang.NonNull" })); BASE_COPYABLE_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { "lombok.NonNull", @@ -93,6 +94,7 @@ public class HandlerUtil { "org.jetbrains.annotations.NotNull", "android.support.annotation.NonNull", "org.eclipse.jdt.annotation.NonNull", + "org.springframework.lang.NonNull", "javax.annotation.Nullable", "javax.annotation.CheckForNull", "edu.umd.cs.findbugs.annotations.UnknownNullness", @@ -100,6 +102,7 @@ public class HandlerUtil { "org.jetbrains.annotations.Nullable", "android.support.annotation.Nullable", "org.eclipse.jdt.annotation.Nullable", + "org.springframework.lang.Nullable" })); } diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index 7cd2e400..1ba26338 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -34,6 +34,7 @@ import lombok.Lombok; import lombok.core.AST; import lombok.core.LombokImmutableList; import lombok.eclipse.handlers.EclipseHandlerUtil; +import lombok.permit.Permit; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Path; @@ -497,7 +498,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { Throwable problem_ = null; Method m = null; try { - m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class); + m = Permit.getMethod(EclipseAstProblemView.class, "addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class); } catch (Throwable t) { // That's problematic, but as long as no local classes are used we don't actually need it. // Better fail on local classes than crash altogether. diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java index 323fc171..e5edba64 100644 --- a/src/core/lombok/eclipse/TransformEclipseAST.java +++ b/src/core/lombok/eclipse/TransformEclipseAST.java @@ -30,6 +30,7 @@ import lombok.core.LombokConfiguration; import lombok.core.debug.DebugSnapshotStore; import lombok.core.debug.HistogramTracker; import lombok.patcher.Symbols; +import lombok.permit.Permit; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -90,7 +91,7 @@ public class TransformEclipseAST { disableLombok = true; } try { - f = CompilationUnitDeclaration.class.getDeclaredField("$lombokAST"); + f = Permit.getField(CompilationUnitDeclaration.class, "$lombokAST"); } catch (Throwable t) { //I guess we're in an ecj environment; we'll just not cache stuff then. } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 5d582aad..c69b3d0f 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -56,6 +56,7 @@ import lombok.eclipse.EclipseAST; import lombok.eclipse.EclipseNode; import lombok.experimental.Accessors; import lombok.experimental.Tolerate; +import lombok.permit.Permit; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -337,9 +338,7 @@ public class EclipseHandlerUtil { private static Field getField(Class<?> c, String fName) { try { - Field f = c.getDeclaredField(fName); - f.setAccessible(true); - return f; + return Permit.getField(c, fName); } catch (Exception e) { return null; } @@ -1931,12 +1930,12 @@ public class EclipseHandlerUtil { Constructor<IntLiteral> intLiteralConstructor_ = null; Method intLiteralFactoryMethod_ = null; try { - intLiteralConstructor_ = IntLiteral.class.getConstructor(parameterTypes); + intLiteralConstructor_ = Permit.getConstructor(IntLiteral.class, parameterTypes); } catch (Throwable ignore) { // probably eclipse 3.7++ } try { - intLiteralFactoryMethod_ = IntLiteral.class.getMethod("buildIntLiteral", parameterTypes); + intLiteralFactoryMethod_ = Permit.getMethod(IntLiteral.class, "buildIntLiteral", parameterTypes); } catch (Throwable ignore) { // probably eclipse versions before 3.7 } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 86c1da42..0ce1436d 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -25,6 +25,7 @@ import static lombok.eclipse.Eclipse.*; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -46,6 +47,7 @@ import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; @@ -105,7 +107,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return ((Boolean) expr).booleanValue(); } - private static class BuilderFieldData { + static class BuilderFieldData { Annotation[] annotations; TypeReference type; char[] rawName; @@ -443,6 +445,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { cleanDecl.declarationSourceEnd = -1; cleanDecl.modifiers = ClassFileConstants.AccPrivate; cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); + cleanDecl.traverse(new SetGeneratedByVisitor(ast), (MethodScope) null); injectFieldAndMarkGenerated(builderType, cleanDecl); } @@ -502,15 +505,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } - private static final char[] EMPTY_LIST = "emptyList".toCharArray(); + private static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'}; private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source) { - // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b); - int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; - MethodDeclaration out = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.selector = methodName.toCharArray(); out.modifiers = ClassFileConstants.AccPublic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -519,6 +519,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); Expression receiver = invoke; + List<Statement> statements = null; for (BuilderFieldData bfd : builderFields) { char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray(); MessageSend ms = new MessageSend(); @@ -552,22 +553,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { tgt[i] = obtainExpr; } } + + ms.selector = setterName; if (bfd.singularData == null) { ms.arguments = tgt; + ms.receiver = receiver; + receiver = ms; } else { - Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); - MessageSend emptyList = new MessageSend(); - emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray()); - emptyList.selector = EMPTY_LIST; - emptyList.typeArguments = bfd.singularData.getSingularizer().createTypeArgs(bfd.singularData.getTypeArgs().size(), false, type, bfd.singularData.getTypeArgs()); - ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])}; + ms.arguments = new Expression[] {tgt[1]}; + ms.receiver = new SingleNameReference(BUILDER_TEMP_VAR, p); + EqualExpression isNotNull = new EqualExpression(tgt[0], new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL); + if (statements == null) statements = new ArrayList<Statement>(); + statements.add(new IfStatement(isNotNull, ms, pS, pE)); } - ms.receiver = receiver; - ms.selector = setterName; - receiver = ms; } - out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)}; + if (statements != null) { + out.statements = new Statement[statements.size() + 2]; + for (int i = 0; i < statements.size(); i++) out.statements[i + 1] = statements.get(i); + LocalDeclaration b = new LocalDeclaration(BUILDER_TEMP_VAR, pS, pE); + out.statements[0] = b; + b.modifiers |= Modifier.FINAL; + b.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); + b.type.sourceStart = pS; b.type.sourceEnd = pE; + b.initialization = receiver; + out.statements[out.statements.length - 1] = new ReturnStatement(new SingleNameReference(BUILDER_TEMP_VAR, p), pS, pE); + } else { + out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)}; + } out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; @@ -627,7 +640,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); args.add(new ConditionalExpression( - new SingleNameReference(bfd.nameOfSetFlag, 0L), + new SingleNameReference(bfd.nameOfSetFlag, 0L), new SingleNameReference(bfd.name, 0L), inv)); } else { diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index cb07115a..82859e4b 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -484,6 +484,7 @@ public class HandleConstructor { for (EclipseNode node : type.down()) { if (node.getKind() != Kind.FIELD) continue top; FieldDeclaration fd = (FieldDeclaration) node.get(); + if (fd.initialization != null) continue top; if ((fd.modifiers & ClassFileConstants.AccFinal) == 0) continue top; if ((fd.modifiers & ClassFileConstants.AccStatic) != 0) continue top; for (EclipseNode ftp : fieldsToParam) if (node == ftp) continue top; diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 3070f1dc..9a3275c2 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -39,7 +39,9 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; @@ -48,6 +50,8 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; @@ -56,6 +60,7 @@ import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.SuperReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; @@ -87,6 +92,7 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; +import lombok.eclipse.handlers.HandleBuilder.BuilderFieldData; import lombok.experimental.NonFinal; import lombok.experimental.SuperBuilder; @@ -98,41 +104,36 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { private static final char[] DEFAULT_PREFIX = "$default$".toCharArray(); private static final char[] SET_PREFIX = "$set".toCharArray(); private static final char[] SELF_METHOD_NAME = "self".toCharArray(); + private static final String TO_BUILDER_METHOD_NAME_STRING = "toBuilder"; + private static final char[] TO_BUILDER_METHOD_NAME = TO_BUILDER_METHOD_NAME_STRING.toCharArray(); + private static final char[] FILL_VALUES_METHOD_NAME = "$fillValuesFrom".toCharArray(); + private static final char[] FILL_VALUES_STATIC_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder".toCharArray(); + private static final char[] EMPTY_LIST = "emptyList".toCharArray(); + private static final char[] INSTANCE_VARIABLE_NAME = "instance".toCharArray(); + private static final String BUILDER_VARIABLE_NAME_STRING = "b"; + private static final char[] BUILDER_VARIABLE_NAME = BUILDER_VARIABLE_NAME_STRING.toCharArray(); private static final AbstractMethodDeclaration[] EMPTY_METHODS = {}; - private static class BuilderFieldData { - Annotation[] annotations; - TypeReference type; - char[] rawName; - char[] name; - char[] nameOfDefaultProvider; - char[] nameOfSetFlag; - SingularData singularData; - ObtainVia obtainVia; - EclipseNode obtainViaNode; - EclipseNode originalFieldNode; - - List<EclipseNode> createdFields = new ArrayList<EclipseNode>(); - } - @Override public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); long p = (long) ast.sourceStart << 32 | ast.sourceEnd; - SuperBuilder builderInstance = annotation.getInstance(); + SuperBuilder superbuilderAnnotation = annotation.getInstance(); - String builderMethodName = builderInstance.builderMethodName(); - String buildMethodName = builderInstance.buildMethodName(); + String builderMethodName = superbuilderAnnotation.builderMethodName(); + String buildMethodName = superbuilderAnnotation.buildMethodName(); if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; - + + boolean toBuilder = superbuilderAnnotation.toBuilder(); + EclipseNode tdParent = annotationNode.up(); java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); @@ -258,14 +259,28 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, superclassBuilderClass != null); - // Create the abstract builder class. + // Create the abstract builder class, or reuse an existing one. EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, typeParams, ast, classGenericName, builderGenericName); } else { - annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); - return; + TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); + if ((builderTypeDeclaration.modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract)) == 0) { + annotationNode.addError("Existing Builder must be an abstract static inner class."); + return; + } + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); + // Generate errors for @Singular BFDs that have one already defined node. + for (BuilderFieldData bfd : builderFields) { + SingularData sd = bfd.singularData; + if (sd == null) continue; + EclipseSingularizer singularizer = sd.getSingularizer(); + if (singularizer == null) continue; + if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) { + bfd.singularData = null; + } + } } // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary. @@ -297,7 +312,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); injectFieldAndMarkGenerated(builderType, cleanDecl); } - + + if (toBuilder) { + // Generate $fillValuesFrom() method in the abstract builder. + injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderClassName, typeParams)); + // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class. + injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast)); + } + // Generate abstract self() and build() methods in the abstract builder. injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName)); injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast)); @@ -324,23 +346,44 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); - if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) { + boolean isAbstract = (td.modifiers & ClassFileConstants.AccAbstract) != 0; + if (isAbstract) { // Only non-abstract classes get the Builder implementation. return; } - // Create the builder implementation class. + // Create the builder implementation class, or reuse an existing one. EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast); } else { - annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); - return; + TypeDeclaration builderImplTypeDeclaration = (TypeDeclaration) builderImplType.get(); + if ((builderImplTypeDeclaration.modifiers & ClassFileConstants.AccAbstract) != 0 || + (builderImplTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { + annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class."); + return; + } + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode); } - + + if (toBuilder) { + // Add the toBuilder() method to the annotated class. + switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) { + case EXISTS_BY_USER: + annotationNode.addWarning("Not generating toBuilder() as it already exists."); + break; + case NOT_EXISTS: + injectMethod(tdParent, generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast)); + default: + // Should not happen. + } + } + // Create the self() and build() methods in the BuilderImpl. injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams, p)); - injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast)); + if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { + injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast)); + } // Add the builder() method to the annotated class. if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { @@ -441,7 +484,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { constructor.selector = typeDeclaration.name; if (callBuilderBasedSuperConstructor) { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); - constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; + constructor.constructorCall.arguments = new Expression[] {new SingleNameReference(BUILDER_VARIABLE_NAME, p)}; } else { constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); } @@ -455,7 +498,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)}; TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p); - constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, builderType, Modifier.FINAL)}; + constructor.arguments = new Argument[] {new Argument(BUILDER_VARIABLE_NAME, p, builderType, Modifier.FINAL)}; List<Statement> statements = new ArrayList<Statement>(); @@ -468,10 +511,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { Expression assignmentExpr; if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { - fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b"); + fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, BUILDER_VARIABLE_NAME_STRING); assignmentExpr = new SingleNameReference(fieldNode.name, p); } else { - char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; + char[][] variableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldName}; long[] positions = new long[] {p, p}; assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); } @@ -479,7 +522,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { // In case of @Builder.Default, set the value to the default if it was NOT set in the builder. if (fieldNode.nameOfSetFlag != null) { - char[][] setVariableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag}; + char[][] setVariableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldNode.nameOfSetFlag}; long[] positions = new long[] {p, p}; QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e); @@ -533,6 +576,166 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { return out; } + /** + * Generates a <code>toBuilder()</code> method in the annotated class that looks like this: + * <pre> + * public ParentBuilder<?, ?> toBuilder() { + * return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this); + * } + * </pre> + */ + private MethodDeclaration generateToBuilderMethod(String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); + out.selector = TO_BUILDER_METHOD_NAME; + out.modifiers = ClassFileConstants.AccPublic; + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + + TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) }; + out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p); + + AllocationExpression newClass = new AllocationExpression(); + newClass.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p); + MessageSend invokeFillMethod = new MessageSend(); + invokeFillMethod.receiver = newClass; + invokeFillMethod.selector = FILL_VALUES_METHOD_NAME; + invokeFillMethod.arguments = new Expression[] {new ThisReference(0, 0)}; + out.statements = new Statement[] {new ReturnStatement(invokeFillMethod, pS, pE)}; + + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); + return out; + } + + /** + * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks + * like this: + * <pre> + * protected B $fillValuesFrom(final C instance) { + * super.$fillValuesFrom(instance); + * FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this); + * return self(); + * } + * </pre> + */ + private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderClassName, TypeParameter[] typeParams) { + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + out.selector = FILL_VALUES_METHOD_NAME; + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + out.modifiers = ClassFileConstants.AccProtected; + if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())}; + out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0); + + TypeReference builderType = new SingleTypeReference(classGenericName.toCharArray(), 0); + out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, builderType, Modifier.FINAL)}; + + List<Statement> body = new ArrayList<Statement>(); + + if (inherited) { + // Call super. + MessageSend callToSuper = new MessageSend(); + callToSuper.receiver = new SuperReference(0, 0); + callToSuper.selector = FILL_VALUES_METHOD_NAME; + callToSuper.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0)}; + body.add(callToSuper); + } + + // Call the builder implemention's helper method that actually fills the values from the instance. + MessageSend callStaticFillValuesMethod = new MessageSend(); + callStaticFillValuesMethod.receiver = new SingleNameReference(builderClassName.toCharArray(), 0); + callStaticFillValuesMethod.selector = FILL_VALUES_STATIC_METHOD_NAME; + callStaticFillValuesMethod.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0), new ThisReference(0, 0)}; + body.add(callStaticFillValuesMethod); + + // Return self(). + MessageSend returnCall = new MessageSend(); + returnCall.receiver = ThisReference.implicitThis(); + returnCall.selector = SELF_METHOD_NAME; + body.add(new ReturnStatement(returnCall, 0, 0)); + + out.statements = body.isEmpty() ? null : body.toArray(new Statement[body.size()]); + + return out; + } + + /** + * Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in + * the builder implementation class that copies all fields from the instance + * to the builder. It looks like this: + * + * <pre> + * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder<?, ?> b) { + * b.field(instance.field); + * } + * </pre> + */ + private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) { + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); + out.selector = FILL_VALUES_STATIC_METHOD_NAME; + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; + out.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); + + TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)}; + TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, 0); + Argument builderArgument = new Argument(BUILDER_VARIABLE_NAME, 0, builderType, Modifier.FINAL); + TypeReference parentArgument = createTypeReferenceWithTypeParameters(tdParent.getName(), typeParams); + out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, parentArgument, Modifier.FINAL), builderArgument}; + + // Add type params if there are any. + if (typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source); + + List<Statement> body = new ArrayList<Statement>(); + + // Call the builder's setter methods to fill the values from the instance. + for (BuilderFieldData bfd : builderFields) { + MessageSend exec = createSetterCallWithInstanceValue(bfd, tdParent, source); + body.add(exec); + } + + out.statements = body.isEmpty() ? null : body.toArray(new Statement[body.size()]); + + return out; + } + + private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, EclipseNode type, ASTNode source) { + char[] setterName = bfd.name; + MessageSend ms = new MessageSend(); + Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2]; + + if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { + char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray(); + for (int i = 0; i < tgt.length; i++) { + FieldReference fr = new FieldReference(fieldName, 0); + fr.receiver = new SingleNameReference(INSTANCE_VARIABLE_NAME, 0); + tgt[i] = fr; + } + } else { + String obtainName = bfd.obtainVia.method(); + boolean obtainIsStatic = bfd.obtainVia.isStatic(); + for (int i = 0; i < tgt.length; i++) { + MessageSend obtainExpr = new MessageSend(); + obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new SingleNameReference(INSTANCE_VARIABLE_NAME, 0); + obtainExpr.selector = obtainName.toCharArray(); + if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0)}; + tgt[i] = obtainExpr; + } + } + if (bfd.singularData == null) { + ms.arguments = tgt; + } else { + Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); + MessageSend emptyList = new MessageSend(); + emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray()); + emptyList.selector = EMPTY_LIST; + ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])}; + } + ms.receiver = new SingleNameReference(BUILDER_VARIABLE_NAME, 0); + ms.selector = setterName; + return ms; + } + private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult); out.selector = SELF_METHOD_NAME; diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 17fc5e09..4b094a9f 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -94,6 +94,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { buildField.modifiers = ClassFileConstants.AccPrivate; buildField.declarationSourceEnd = -1; buildField.type = type; + data.setGeneratedByRecursive(buildField); return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } @@ -117,6 +118,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.returnType = returnType; md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + data.setGeneratedByRecursive(md); injectMethod(builderType, md); } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index c7315790..11314bd3 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -85,6 +85,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula buildField.modifiers = ClassFileConstants.AccPrivate; buildField.declarationSourceEnd = -1; buildField.type = type; + data.setGeneratedByRecursive(buildField); return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } @@ -117,6 +118,8 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement}; md.returnType = returnType; md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + + data.setGeneratedByRecursive(md); injectMethod(builderType, md); } @@ -149,7 +152,6 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; data.setGeneratedByRecursive(md); - HandleNonNull.INSTANCE.fix(injectMethod(builderType, md)); } diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index 174cd5fc..55f6cadd 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -127,6 +127,7 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer buildValueField.declarationSourceEnd = -1; buildValueField.type = type; } + data.setGeneratedByRecursive(buildKeyField); data.setGeneratedByRecursive(buildValueField); EclipseNode keyFieldNode = injectFieldAndMarkGenerated(builderType, buildKeyField); @@ -174,6 +175,7 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer md.returnType = returnType; md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + data.setGeneratedByRecursive(md); injectMethod(builderType, md); } diff --git a/src/core/lombok/experimental/SuperBuilder.java b/src/core/lombok/experimental/SuperBuilder.java index 26127a5f..be6ea304 100644 --- a/src/core/lombok/experimental/SuperBuilder.java +++ b/src/core/lombok/experimental/SuperBuilder.java @@ -1,16 +1,16 @@ /* * Copyright (C) 2018 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 @@ -43,7 +43,7 @@ import lombok.Singular; * The builder also has a <code>build()</code> method which returns a completed instance of the original type. * <p> * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/SuperBuilder">the project lombok features page for @SuperBuilder</a>. - * + * * @see Singular */ @Target(TYPE) @@ -51,16 +51,15 @@ import lombok.Singular; public @interface SuperBuilder { /** @return Name of the method that creates a new builder instance. Default: {@code builder}. */ String builderMethodName() default "builder"; - + /** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ String buildMethodName() default "build"; - - // toBuilder also requires a two-stage system where each class gets its own toBuilder but calls on a second method (and also calls parentclass's method) - // to fill the builder, as this class does not know what fields to pass on to the builder. Let's consider this, but only for milestone 2. - /* - * If true, generate an instance method to obtain a builder that is initialized with the values of this instance. - * + + /** + * If <code>true</code>, generate an instance method to obtain a builder that is initialized with the values of this instance. + * In this case, all superclasses must also have <code>toBuilder=true</code>. + * * @return Whether to generate a {@code toBuilder()} method. */ -// boolean toBuilder() default false; + boolean toBuilder() default false; } diff --git a/src/core/lombok/javac/CompilerMessageSuppressor.java b/src/core/lombok/javac/CompilerMessageSuppressor.java index 391ec64a..02dc6c26 100644 --- a/src/core/lombok/javac/CompilerMessageSuppressor.java +++ b/src/core/lombok/javac/CompilerMessageSuppressor.java @@ -39,6 +39,8 @@ import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; +import lombok.permit.Permit; + /** * During resolution, the resolver will emit resolution errors, but without appropriate file names and line numbers. If these resolution errors stick around * then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger. @@ -89,11 +91,8 @@ public final class CompilerMessageSuppressor { static Field getDeclaredField(Class<?> c, String fieldName) { try { - Field field = c.getDeclaredField(fieldName); - field.setAccessible(true); - return field; - } - catch (Throwable t) { + return Permit.getField(c, fieldName); + } catch (Throwable t) { return null; } } diff --git a/src/core/lombok/javac/Javac6BasedLombokOptions.java b/src/core/lombok/javac/Javac6BasedLombokOptions.java index cefb89ff..fc73181c 100644 --- a/src/core/lombok/javac/Javac6BasedLombokOptions.java +++ b/src/core/lombok/javac/Javac6BasedLombokOptions.java @@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import lombok.Lombok; +import lombok.permit.Permit; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Options; @@ -36,8 +37,8 @@ public class Javac6BasedLombokOptions extends LombokOptions { static { try { Class<?> optionNameClass = Class.forName("com.sun.tools.javac.main.OptionName"); - optionName_valueOf = optionNameClass.getMethod("valueOf", String.class); - options_put = Class.forName("com.sun.tools.javac.util.Options").getMethod("put", optionNameClass, String.class); + optionName_valueOf = Permit.getMethod(optionNameClass, "valueOf", String.class); + options_put = Permit.getMethod(Class.forName("com.sun.tools.javac.util.Options"), "put", optionNameClass, String.class); } catch (Exception e) { throw new IllegalArgumentException("Can't initialize Javac6-based lombok options due to reflection issue.", e); } diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index 091612cc..9a5305a6 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -37,6 +37,7 @@ import javax.tools.JavaFileObject; import com.sun.tools.javac.util.JCDiagnostic; import lombok.core.AST; +import lombok.permit.Permit; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Symtab; @@ -243,7 +244,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { private static List<JCTree> getResourcesForTryNode(JCTry tryNode) { if (!JCTRY_RESOURCES_FIELD_INITIALIZED) { try { - JCTRY_RESOURCES_FIELD = JCTry.class.getField("resources"); + JCTRY_RESOURCES_FIELD = Permit.getField(JCTry.class, "resources"); } catch (NoSuchFieldException ignore) { // Java 1.6 or lower won't have this at all. } catch (Exception ignore) { @@ -343,7 +344,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return m; } try { - m = c.getMethod("getBody"); + m = Permit.getMethod(c, "getBody"); } catch (NoSuchMethodException e) { throw Javac.sneakyThrow(e); } @@ -501,12 +502,11 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } static ErrorLog create(Messager messager, Log log) { - Field errorCount = null; - try { - Field f = messager.getClass().getDeclaredField("errorCount"); - f.setAccessible(true); - errorCount = f; - } catch (Throwable t) {} + Field errorCount; try { + errorCount = Permit.getField(messager.getClass(), "errorCount"); + } catch (Throwable t) { + errorCount = null; + } boolean hasMultipleErrors = false; for (Field field : log.getClass().getFields()) { if (field.getName().equals("multipleErrors")) { @@ -515,14 +515,13 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } } if (hasMultipleErrors) return new JdkBefore9(log, messager, errorCount); - - Field warningCount = null; - try { - Field f = messager.getClass().getDeclaredField("warningCount"); - f.setAccessible(true); - warningCount = f; - } catch (Throwable t) {} - + + Field warningCount; try { + warningCount = Permit.getField(messager.getClass(), "warningCount"); + } catch (Throwable t) { + warningCount = null; + } + return new Jdk9Plus(log, messager, errorCount, warningCount); } } @@ -577,21 +576,18 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { Class<?> noteCls = Class.forName(jcd + "$Note"); Class<?> lc = log.getClass(); - this.errorMethod = lc.getMethod("error", df, DiagnosticPosition.class, errorCls); - this.warningMethod = lc.getMethod("warning", DiagnosticPosition.class, warningCls); - this.mandatoryWarningMethod = lc.getMethod("mandatoryWarning", DiagnosticPosition.class, warningCls); - this.noteMethod = lc.getMethod("note", DiagnosticPosition.class, noteCls); + this.errorMethod = Permit.getMethod(lc, "error", df, DiagnosticPosition.class, errorCls); + this.warningMethod = Permit.getMethod(lc, "warning", DiagnosticPosition.class, warningCls); + this.mandatoryWarningMethod = Permit.getMethod(lc, "mandatoryWarning", DiagnosticPosition.class, warningCls); + this.noteMethod = Permit.getMethod(lc, "note", DiagnosticPosition.class, noteCls); - Field diagsField = lc.getSuperclass().getDeclaredField("diags"); - diagsField.setAccessible(true); - this.diags = (JCDiagnostic.Factory)diagsField.get(log); + Field diagsField = Permit.getField(lc.getSuperclass(), "diags"); + this.diags = (JCDiagnostic.Factory) diagsField.get(log); Class<?> dc = this.diags.getClass(); - this.errorKey = dc.getMethod("errorKey", String.class, Object[].class); - this.warningKey = dc.getDeclaredMethod("warningKey", String.class, Object[].class); - this.warningKey.setAccessible(true); - this.noteKey = dc.getDeclaredMethod("noteKey", String.class, Object[].class); - this.noteKey.setAccessible(true); + this.errorKey = Permit.getMethod(dc, "errorKey", String.class, Object[].class); + this.warningKey = Permit.getMethod(dc, "warningKey", String.class, Object[].class); + this.noteKey = Permit.getMethod(dc, "noteKey", String.class, Object[].class); } catch (Throwable t) { //t.printStackTrace(); } diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java index 8cc239e1..abbf6726 100644 --- a/src/core/lombok/javac/JavacResolution.java +++ b/src/core/lombok/javac/JavacResolution.java @@ -35,6 +35,7 @@ import javax.tools.JavaFileObject; import lombok.Lombok; import lombok.core.debug.AssertionLogger; +import lombok.permit.Permit; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Symbol.TypeSymbol; @@ -162,14 +163,10 @@ public class JavacResolution { private static Field getMemberEnterDotEnv() { if (memberEnterDotEnv != null) return memberEnterDotEnv; try { - Field f = MemberEnter.class.getDeclaredField("env"); - f.setAccessible(true); - memberEnterDotEnv = f; + return memberEnterDotEnv = Permit.getField(MemberEnter.class, "env"); } catch (NoSuchFieldException e) { return null; } - - return memberEnterDotEnv; } @SuppressWarnings("unchecked") @@ -252,10 +249,10 @@ public class JavacResolution { static { Method upperBound = null; try { - upperBound = Types.class.getMethod("upperBound", Type.class); + upperBound = Permit.getMethod(Types.class, "upperBound", Type.class); } catch (Throwable ignore) {} if (upperBound == null) try { - upperBound = Types.class.getMethod("wildUpperBound", Type.class); + upperBound = Permit.getMethod(Types.class, "wildUpperBound", Type.class); } catch (Throwable ignore) {} UPPER_BOUND = upperBound; diff --git a/src/core/lombok/javac/apt/LombokFileObjects.java b/src/core/lombok/javac/apt/LombokFileObjects.java index aba10540..28d3c9fa 100644 --- a/src/core/lombok/javac/apt/LombokFileObjects.java +++ b/src/core/lombok/javac/apt/LombokFileObjects.java @@ -39,6 +39,7 @@ import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import lombok.core.DiagnosticsReceiver; +import lombok.permit.Permit; import com.sun.tools.javac.file.BaseFileManager; @@ -87,16 +88,14 @@ final class LombokFileObjects { } static Method getDecoderMethod(String className) { - Method m = null; try { - m = Class.forName(className).getDeclaredMethod("getDecoder", boolean.class); - m.setAccessible(true); + return Permit.getMethod(Class.forName(className), "getDecoder", boolean.class); } catch (NoSuchMethodException e) { // Intentional fallthrough - getDecoder(boolean) is not always present. } catch (ClassNotFoundException e) { // Intentional fallthrough - getDecoder(boolean) is not always present. } - return m; + return null; } private LombokFileObjects() {} diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index 247d0560..a3d1dfcf 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -52,6 +52,7 @@ import javax.tools.JavaFileObject; import lombok.Lombok; import lombok.core.DiagnosticsReceiver; import lombok.javac.JavacTransformer; +import lombok.permit.Permit; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; @@ -112,10 +113,7 @@ public class LombokProcessor extends AbstractProcessor { private static final Field getFieldAccessor(String typeName, String fieldName) { try { - Class<?> c = Class.forName(typeName); - Field f = c.getDeclaredField(fieldName); - f.setAccessible(true); - return f; + return Permit.getField(Class.forName(typeName), fieldName); } catch (ClassNotFoundException e) { return null; } catch (NoSuchFieldException e) { @@ -156,11 +154,9 @@ public class LombokProcessor extends AbstractProcessor { Context context = javacProcessingEnv.getContext(); disablePartialReparseInNetBeansEditor(context); try { - Method keyMethod = Context.class.getDeclaredMethod("key", Class.class); - keyMethod.setAccessible(true); + Method keyMethod = Permit.getMethod(Context.class, "key", Class.class); Object key = keyMethod.invoke(context, JavaFileManager.class); - Field htField = Context.class.getDeclaredField("ht"); - htField.setAccessible(true); + Field htField = Permit.getField(Context.class, "ht"); @SuppressWarnings("unchecked") Map<Object,Object> ht = (Map<Object,Object>) htField.get(context); final JavaFileManager originalFiler = (JavaFileManager) ht.get(key); @@ -170,8 +166,7 @@ public class LombokProcessor extends AbstractProcessor { JavaFileManager newFilerManager = new InterceptingJavaFileManager(originalFiler, receiver); ht.put(key, newFilerManager); - Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager"); - filerFileManagerField.setAccessible(true); + Field filerFileManagerField = Permit.getField(JavacFiler.class, "fileManager"); filerFileManagerField.set(javacFiler, newFilerManager); if (lombok.javac.Javac.getJavaCompilerVersion() > 8 @@ -186,20 +181,17 @@ public class LombokProcessor extends AbstractProcessor { private void replaceFileManagerJdk9(Context context, JavaFileManager newFiler) { try { - JavaCompiler compiler = (JavaCompiler) JavaCompiler.class.getDeclaredMethod("instance", Context.class).invoke(null, context); + JavaCompiler compiler = (JavaCompiler) Permit.getMethod(JavaCompiler.class, "instance", Context.class).invoke(null, context); try { - Field fileManagerField = JavaCompiler.class.getDeclaredField("fileManager"); - fileManagerField.setAccessible(true); + Field fileManagerField = Permit.getField(JavaCompiler.class, "fileManager"); fileManagerField.set(compiler, newFiler); } catch (Exception e) {} try { - Field writerField = JavaCompiler.class.getDeclaredField("writer"); - writerField.setAccessible(true); + Field writerField = Permit.getField(JavaCompiler.class, "writer"); ClassWriter writer = (ClassWriter) writerField.get(compiler); - Field fileManagerField = ClassWriter.class.getDeclaredField("fileManager"); - fileManagerField.setAccessible(true); + Field fileManagerField = Permit.getField(ClassWriter.class, "fileManager"); fileManagerField.set(writer, newFiler); } catch (Exception e) {} @@ -210,8 +202,7 @@ public class LombokProcessor extends AbstractProcessor { private void forceMultipleRoundsInNetBeansEditor() { try { - Field f = JavacProcessingEnvironment.class.getDeclaredField("isBackgroundCompilation"); - f.setAccessible(true); + Field f = Permit.getField(JavacProcessingEnvironment.class, "isBackgroundCompilation"); f.set(javacProcessingEnv, true); } catch (NoSuchFieldException e) { // only NetBeans has it @@ -223,14 +214,12 @@ public class LombokProcessor extends AbstractProcessor { private void disablePartialReparseInNetBeansEditor(Context context) { try { Class<?> cancelServiceClass = Class.forName("com.sun.tools.javac.util.CancelService"); - Method cancelServiceInstace = cancelServiceClass.getDeclaredMethod("instance", Context.class); + Method cancelServiceInstace = Permit.getMethod(cancelServiceClass, "instance", Context.class); Object cancelService = cancelServiceInstace.invoke(null, context); if (cancelService == null) return; - Field parserField = cancelService.getClass().getDeclaredField("parser"); - parserField.setAccessible(true); + Field parserField = Permit.getField(cancelService.getClass(), "parser"); Object parser = parserField.get(cancelService); - Field supportsReparseField = parser.getClass().getDeclaredField("supportsReparse"); - supportsReparseField.setAccessible(true); + Field supportsReparseField = Permit.getField(parser.getClass(), "supportsReparse"); supportsReparseField.set(parser, false); } catch (ClassNotFoundException e) { // only NetBeans has it @@ -284,8 +273,7 @@ public class LombokProcessor extends AbstractProcessor { private void stopJavacProcessingEnvironmentFromClosingOurClassloader() { try { - Field f = JavacProcessingEnvironment.class.getDeclaredField("processorClassLoader"); - f.setAccessible(true); + Field f = Permit.getField(JavacProcessingEnvironment.class, "processorClassLoader"); ClassLoader unwrapped = (ClassLoader) f.get(javacProcessingEnv); if (unwrapped == null) return; ClassLoader wrapped = wrapClassLoader(unwrapped); @@ -377,7 +365,7 @@ public class LombokProcessor extends AbstractProcessor { } catch (Exception e) { e.printStackTrace(); processingEnv.getMessager().printMessage(Kind.WARNING, - "Can't force a new processing round. Lombok won't work."); + "Can't force a new processing round. Lombok won't work."); } } } @@ -414,16 +402,14 @@ public class LombokProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } - + /** * This class casts the given processing environment to a JavacProcessingEnvironment. In case of * gradle incremental compilation, the delegate ProcessingEnvironment of the gradle wrapper is returned. */ public JavacProcessingEnvironment getJavacProcessingEnvironment(Object procEnv) { - if (procEnv instanceof JavacProcessingEnvironment) { - return (JavacProcessingEnvironment) procEnv; - } - + if (procEnv instanceof JavacProcessingEnvironment) return (JavacProcessingEnvironment) procEnv; + // try to find a "delegate" field in the object, and use this to try to obtain a JavacProcessingEnvironment for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) { try { @@ -432,9 +418,9 @@ public class LombokProcessor extends AbstractProcessor { // delegate field was not found, try on superclass } } - + processingEnv.getMessager().printMessage(Kind.WARNING, - "Can't get the delegate of the gradle IncrementalProcessingEnvironment. Lombok won't work."); + "Can't get the delegate of the gradle IncrementalProcessingEnvironment. Lombok won't work."); return null; } @@ -444,10 +430,8 @@ public class LombokProcessor extends AbstractProcessor { * (directly or through a delegate field again) */ public JavacFiler getJavacFiler(Object filer) { - if (filer instanceof JavacFiler) { - return (JavacFiler) filer; - } - + if (filer instanceof JavacFiler) return (JavacFiler) filer; + // try to find a "delegate" field in the object, and use this to check for a JavacFiler for (Class<?> filerClass = filer.getClass(); filerClass != null; filerClass = filerClass.getSuperclass()) { try { @@ -456,15 +440,13 @@ public class LombokProcessor extends AbstractProcessor { // delegate field was not found, try on superclass } } - + processingEnv.getMessager().printMessage(Kind.WARNING, - "Can't get a JavacFiler from " + filer.getClass().getName() + ". Lombok won't work."); + "Can't get a JavacFiler from " + filer.getClass().getName() + ". Lombok won't work."); return null; } private Object tryGetDelegateField(Class<?> delegateClass, Object instance) throws Exception { - Field field = delegateClass.getDeclaredField("delegate"); - field.setAccessible(true); - return field.get(instance); + return Permit.getField(delegateClass, "delegate").get(instance); } } diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java index 7a187148..f15bf74c 100644 --- a/src/core/lombok/javac/apt/Processor.java +++ b/src/core/lombok/javac/apt/Processor.java @@ -53,6 +53,8 @@ import com.sun.tools.javac.processing.JavacFiler; import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.util.Options; +import lombok.permit.Permit; + /** * This processor should not be used. It used to be THE processor. This class is only there to warn people that something went wrong, and for the * lombok developers to see if what the reason for those failures is. @@ -101,8 +103,7 @@ public class Processor extends AbstractProcessor { try { JavacProcessingEnvironment environment = (JavacProcessingEnvironment) procEnv; Options instance = Options.instance(environment.getContext()); - Field field = Options.class.getDeclaredField("values"); - field.setAccessible(true); + Field field = Permit.getField(Options.class, "values"); @SuppressWarnings("unchecked") Map<String, String> values = (Map<String, String>) field.get(instance); if (values.isEmpty()) { message.append("Options: empty\n\n"); @@ -125,8 +126,7 @@ public class Processor extends AbstractProcessor { private void findServices(StringBuilder message, Filer filer) { try { - Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager"); - filerFileManagerField.setAccessible(true); + Field filerFileManagerField = Permit.getField(JavacFiler.class, "fileManager"); JavaFileManager jfm = (JavaFileManager) filerFileManagerField.get(filer); ClassLoader processorClassLoader = jfm.hasLocation(ANNOTATION_PROCESSOR_PATH) ? jfm.getClassLoader(ANNOTATION_PROCESSOR_PATH) : jfm.getClassLoader(CLASS_PATH); Enumeration<URL> resources = processorClassLoader.getResources("META-INF/services/javax.annotation.processing.Processor"); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 903844d2..0f6e21d0 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<Builder> { return ((Boolean) expr).booleanValue(); } - private static class BuilderFieldData { + static class BuilderFieldData { List<JCAnnotation> annotations; JCExpression type; Name rawName; @@ -484,6 +484,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { sb.append("__ERR__"); } + private static final String BUILDER_TEMP_VAR = "builder"; private JCMethodDecl generateToBuilderMethod(String toBuilderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, boolean fluent, JCAnnotation ast) { // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b); JavacTreeMaker maker = type.getTreeMaker(); @@ -495,6 +496,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null); JCExpression invoke = call; + ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); for (BuilderFieldData bfd : builderFields) { Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString())); JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2]; @@ -519,19 +521,21 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCExpression arg; if (bfd.singularData == null) { arg = tgt[0]; + invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg)); } else { - JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null)); - // Use the singularizer to create the type args (will remove possible wildcards on the type). - List<JCExpression> tas = bfd.singularData.getSingularizer().createTypeArgs(bfd.singularData.getTypeArgs().size(), false, type, bfd.singularData.getTypeArgs(), type.get()); - JCExpression emptyList = maker.Apply(tas, chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil()); - arg = maker.Conditional(eqNull, emptyList, tgt[1]); + JCExpression isNotNull = maker.Binary(CTC_NOT_EQUAL, tgt[0], maker.Literal(CTC_BOT, null)); + JCExpression invokeBuilder = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_TEMP_VAR)), setterName), List.<JCExpression>of(tgt[1])); + statements.append(maker.If(isNotNull, maker.Exec(invokeBuilder), null)); } - - invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg)); } - JCStatement statement = maker.Return(invoke); - - JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); + if (!statements.isEmpty()) { + JCExpression tempVarType = namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams); + statements.prepend(maker.VarDef(maker.Modifiers(Flags.FINAL), type.toName(BUILDER_TEMP_VAR), tempVarType, invoke)); + statements.append(maker.Return(maker.Ident(type.toName(BUILDER_TEMP_VAR)))); + } else { + statements.append(maker.Return(invoke)); + } + JCBlock body = maker.Block(0, statements.toList()); return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 66d6e47e..8af0a7c0 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -27,6 +27,8 @@ import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.ArrayList; +import javax.lang.model.element.Modifier; + import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.BoundKind; @@ -37,11 +39,13 @@ import com.sun.tools.javac.tree.JCTree.JCAssign; 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.JCExpressionStatement; 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; @@ -61,6 +65,7 @@ import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.core.handlers.HandlerUtil; +import lombok.core.handlers.HandlerUtil.FieldAccess; import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.experimental.NonFinal; import lombok.experimental.SuperBuilder; @@ -68,32 +73,23 @@ 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; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; +import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker; @ProviderFor(JavacAnnotationHandler.class) @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<SuperBuilder> { private static final String SELF_METHOD = "self"; - - private static class BuilderFieldData { - List<JCAnnotation> annotations; - JCExpression type; - Name rawName; - Name name; - Name nameOfDefaultProvider; - Name nameOfSetFlag; - SingularData singularData; - ObtainVia obtainVia; - JavacNode obtainViaNode; - JavacNode originalFieldNode; - - java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>(); - } - + private static final String TO_BUILDER_METHOD_NAME = "toBuilder"; + private static final String FILL_VALUES_METHOD_NAME = "$fillValuesFrom"; + private static final String STATIC_FILL_VALUES_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder"; + private static final String INSTANCE_VARIABLE_NAME = "instance"; + private static final String BUILDER_VARIABLE_NAME = "b"; + @Override public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); @@ -110,10 +106,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; + boolean toBuilder = superbuilderAnnotation.toBuilder(); + JavacNode tdParent = annotationNode.up(); java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); - JCExpression returnType; List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrownExceptions = List.nil(); List<JCExpression> superclassTypeParams = List.nil(); @@ -192,7 +189,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // If there is no superclass, superclassBuilderClassExpression is still == null at this point. // You can use it to check whether to inherit or not. - returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; // <C, B> are the generics for our builder. @@ -231,10 +227,25 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = generateBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression, - typeParams, superclassTypeParams, ast, classGenericName, builderGenericName); + typeParams, superclassTypeParams, classGenericName, builderGenericName); } else { - annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); - return; + JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); + if (!builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC) + || !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { + annotationNode.addError("Existing Builder must be an abstract static inner class."); + return; + } + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); + // Generate errors for @Singular BFDs that have one already defined node. + for (BuilderFieldData bfd : builderFields) { + SingularData sd = bfd.singularData; + if (sd == null) continue; + JavacSingularizer singularizer = sd.getSingularizer(); + if (singularizer == null) continue; + if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) { + bfd.singularData = null; + } + } } // Generate the fields in the abstract builder class that hold the values for the instance. @@ -245,6 +256,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { injectFieldAndMarkGenerated(builderType, uncleanField); } + if (toBuilder) { + // Generate $fillValuesFrom() method in the abstract builder. + injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderClassName)); + // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class. + injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields)); + } + // Generate abstract self() and build() methods in the abstract builder. injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName)); injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null, classGenericName)); @@ -271,25 +289,33 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext()); - if ((td.mods.flags & Flags.ABSTRACT) == 0) { + boolean isAbstract = (td.mods.flags & Flags.ABSTRACT) != 0; + if (!isAbstract) { // Only non-abstract classes get the Builder implementation. // Create the builder implementation class. JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { - builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast); + builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams); } else { - annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead."); - return; + JCClassDecl builderImplTypeDeclaration = (JCClassDecl) builderImplType.get(); + if (!builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC) + || builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { + annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class."); + return; + } + sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode); } - + // Create a simple constructor for the BuilderImpl class. JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode); if (cd != null) injectMethod(builderImplType, cd); // Create the self() and build() methods in the BuilderImpl. injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams)); - injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions)); + if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { + injectMethod(builderImplType, generateBuildMethod(buildMethodName, tdParent, builderImplType, thrownExceptions)); + } recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext()); } @@ -298,15 +324,32 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, superclassBuilderClassExpression != null); - if ((td.mods.flags & Flags.ABSTRACT) == 0) { - // Only non-abstract classes get the Builder implementation and the builder() method. + if (isAbstract) { + // Only non-abstract classes get the builder() and toBuilder() methods. + return; + } - // Add the builder() method to the annotated class. - // Allow users to specify their own builder() methods, e.g., to provide default values. - if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); - recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext()); - if (builderMethod != null) injectMethod(tdParent, builderMethod); + // Add the builder() method to the annotated class. + // Allow users to specify their own builder() methods, e.g., to provide default values. + if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { + JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); + recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext()); + if (builderMethod != null) injectMethod(tdParent, builderMethod); + } + + // Add the toBuilder() method to the annotated class. + 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); + } + default: + // Should not happen. } } } @@ -316,7 +359,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { */ private JavacNode generateBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass, JCExpression superclassBuilderClassExpression, List<JCTypeParameter> typeParams, - List<JCExpression> superclassTypeParams, JCAnnotation ast, String classGenericName, String builderGenericName) { + List<JCExpression> superclassTypeParams, String classGenericName, String builderGenericName) { JavacTreeMaker maker = tdParent.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC); @@ -358,7 +401,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { /** * Creates and returns the concrete builder implementation class and injects it into the annotated class. */ - private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams, JCAnnotation ast) { + private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams) { JavacTreeMaker maker = tdParent.getTreeMaker(); JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL); @@ -410,7 +453,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - Name builderVariableName = typeNode.toName("b"); + Name builderVariableName = typeNode.toName(BUILDER_VARIABLE_NAME); for (BuilderFieldData bfd : builderFields) { JCExpression rhs; if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { @@ -496,6 +539,166 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); } + /** + * Generates a <code>toBuilder()</code> method in the annotated class that looks like this: + * <pre> + * public ParentBuilder<?, ?> toBuilder() { + * return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this); + * } + * </pre> + */ + private JCMethodDecl generateToBuilderMethod(String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) { + JavacTreeMaker maker = type.getTreeMaker(); + + ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); + for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name)); + + JCExpression newClass = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null); + List<JCExpression> methodArgs = List.<JCExpression>of(maker.Ident(type.toName("this"))); + JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), methodArgs); + JCStatement statement = maker.Return(invokeFillMethod); + + JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); + int modifiers = Flags.PUBLIC; + + // Add any type params of the annotated class to the return type. + ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>(); + 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, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + } + + /** + * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks + * like this: + * <pre> + * protected B $fillValuesFrom(final C instance) { + * super.$fillValuesFrom(instance); + * FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this); + * return self(); + * } + * </pre> + */ + private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName) { + JavacTreeMaker maker = type.getTreeMaker(); + List<JCAnnotation> annotations = List.nil(); + if (inherited) { + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()); + annotations = List.of(overrideAnnotation); + } + JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, annotations); + Name name = type.toName(FILL_VALUES_METHOD_NAME); + JCExpression returnType = maker.Ident(type.toName(builderGenericName)); + + JCExpression classGenericNameExpr = maker.Ident(type.toName(classGenericName)); + JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null); + + ListBuffer<JCStatement> body = new ListBuffer<JCStatement>(); + + if (inherited) { + // Call super. + JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(), + maker.Select(maker.Ident(type.toName("super")), name), + List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)))); + body.append(maker.Exec(callToSuper)); + } + + // Call the builder implemention's helper method that actually fills the values from the instance. + JCMethodInvocation callStaticFillValuesMethod = maker.Apply(List.<JCExpression>nil(), + maker.Select(maker.Ident(type.toName(builderImplClassName)), type.toName(STATIC_FILL_VALUES_METHOD_NAME)), + List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), maker.Ident(type.toName("this")))); + body.append(maker.Exec(callStaticFillValuesMethod)); + + JCReturn returnStatement = maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName(SELF_METHOD)), List.<JCExpression>nil())); + body.append(returnStatement); + JCBlock bodyBlock = maker.Block(0, body.toList()); + + return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.of(param), List.<JCExpression>nil(), bodyBlock, null); + } + + /** + * Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in + * the builder implementation class that copies all fields from the instance + * to the builder. It looks like this: + * + * <pre> + * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder<?, ?> b) { + * b.field(instance.field); + * } + * </pre> + */ + private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String builderClassname, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields) { + JavacTreeMaker maker = type.getTreeMaker(); + List<JCAnnotation> annotations = List.nil(); + JCModifiers modifiers = maker.Modifiers(Flags.PRIVATE | Flags.STATIC, annotations); + Name name = type.toName(STATIC_FILL_VALUES_METHOD_NAME); + JCExpression returnType = maker.TypeIdent(CTC_VOID); + + // 1st parameter: "Foobar instance" + JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), cloneSelfType(type), null); + + // 2nd parameter: "FoobarBuilder<?, ?> b" (plus generics on the annotated type) + // First add all generics that are present on the parent type. + ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker); + // Now add the <?, ?>. + JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + typeParamsForBuilderParameter.add(wildcard); + wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + typeParamsForBuilderParameter.add(wildcard); + JCTypeApply builderType = maker.TypeApply(maker.Ident(type.toName(builderClassname)), typeParamsForBuilderParameter.toList()); + JCVariableDecl paramBuilder = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(BUILDER_VARIABLE_NAME), builderType, null); + + ListBuffer<JCStatement> body = new ListBuffer<JCStatement>(); + + // Call the builder's setter methods to fill the values from the instance. + for (BuilderFieldData bfd : builderFields) { + JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, type, maker); + body.append(exec); + } + + JCBlock bodyBlock = maker.Block(0, body.toList()); + + return maker.MethodDef(modifiers, name, returnType, copyTypeParams(type, typeParams), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null); + } + + private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, JavacNode type, JavacTreeMaker maker) { + JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2]; + if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { + for (int i = 0; i < tgt.length; i++) { + tgt[i] = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + } + } else { + if (bfd.obtainVia.isStatic()) { + for (int i = 0; i < tgt.length; i++) { + JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method())); + tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)))); + } + } else { + for (int i = 0; i < tgt.length; i++) { + JCExpression c = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), type.toName(bfd.obtainVia.method())); + tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil()); + } + } + } + + JCExpression arg; + if (bfd.singularData == null) { + arg = tgt[0]; + } else { + JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null)); + JCExpression emptyList = maker.Apply(List.<JCExpression>nil(), chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil()); + arg = maker.Conditional(eqNull, emptyList, tgt[1]); + } + JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_VARIABLE_NAME)), bfd.name), List.of(arg)); + JCExpressionStatement exec = maker.Exec(apply); + return exec; + } + private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) { JavacTreeMaker maker = type.getTreeMaker(); List<JCAnnotation> annotations = List.nil(); @@ -506,7 +709,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations); Name name = type.toName(SELF_METHOD); JCExpression returnType = maker.Ident(type.toName(builderGenericName)); - + return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null); } @@ -538,7 +741,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null); } - private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List<JCExpression> thrownExceptions) { + private JCMethodDecl generateBuildMethod(String buildName, JavacNode returnType, JavacNode type, List<JCExpression> thrownExceptions) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -546,7 +749,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Use a constructor that only has this builder as parameter. List<JCExpression> builderArg = List.<JCExpression>of(maker.Ident(type.toName("this"))); - call = maker.NewClass(null, List.<JCExpression>nil(), returnType, builderArg, null); + call = maker.NewClass(null, List.<JCExpression>nil(), cloneSelfType(returnType), builderArg, null); statements.append(maker.Return(call)); JCBlock body = maker.Block(0, statements.toList()); @@ -554,7 +757,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil()); JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation)); - return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); + return maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); } private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) { @@ -618,13 +821,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { }}; if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations); + generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations); } else { fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker); } } - private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam) { + private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; for (JavacNode child : builderType.down()) { diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index e4e40095..c97c7ede 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -58,6 +58,7 @@ import lombok.experimental.Tolerate; import lombok.javac.Javac; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; +import lombok.permit.Permit; import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; @@ -1049,10 +1050,9 @@ public class JavacHandlerUtil { if (TYPE != null) return; if (!in.getName().equals("com.sun.tools.javac.tree.JCTree$JCAnnotatedType")) return; try { - CONSTRUCTOR = in.getDeclaredConstructor(List.class, JCExpression.class); - CONSTRUCTOR.setAccessible(true); - ANNOTATIONS = in.getDeclaredField("annotations"); - UNDERLYING_TYPE = in.getDeclaredField("underlyingType"); + CONSTRUCTOR = Permit.getConstructor(in, List.class, JCExpression.class); + ANNOTATIONS = Permit.getField(in, "annotations"); + UNDERLYING_TYPE = Permit.getField(in, "underlyingType"); TYPE = in; } catch (Exception ignore) {} } @@ -1102,9 +1102,9 @@ public class JavacHandlerUtil { Method r = null; Method e = null; try { - f = ClassSymbol.class.getField("members_field"); - r = f.getType().getMethod("remove", Symbol.class); - e = f.getType().getMethod("enter", Symbol.class); + f = Permit.getField(ClassSymbol.class, "members_field"); + r = Permit.getMethod(f.getType(), "remove", Symbol.class); + e = Permit.getMethod(f.getType(), "enter", Symbol.class); } catch (Exception ex) {} membersField = f; removeMethod = r; |