Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] @Builder.Exclude for a field in cases where @Builder/@SuperBuilder is on a type #3723

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/core/lombok/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@
@Retention(SOURCE)
public @interface Default {}

/**
* The field annotated with {@code @Exclude} will be excluded from the builder. Excluded objects will be initialized as null
* and primitives with their default value.
*/
@Target(FIELD)
@Retention(SOURCE)
@interface Exclude {}

/** @return Name of the method that creates a new builder instance. Default: {@code builder}. If the empty string, suppress generating the {@code builder} method. */
String builderMethodName() default "builder";

Expand Down
11 changes: 10 additions & 1 deletion src/core/lombok/eclipse/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.findAnnotation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import lombok.javac.JavacNode;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
Expand Down Expand Up @@ -228,6 +230,7 @@ static class BuilderFieldData {
ObtainVia obtainVia;
EclipseNode obtainViaNode;
EclipseNode originalFieldNode;
boolean toExclude = false;

List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
}
Expand Down Expand Up @@ -323,6 +326,10 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
bfd.type = fd.type;
bfd.singularData = getSingularData(fieldNode, ast, annInstance.setterPrefix());
bfd.originalFieldNode = fieldNode;
EclipseNode isExcluded = findAnnotation(Builder.Exclude.class, fieldNode);
if (isExcluded != null) {
bfd.toExclude = true;
}

if (bfd.singularData != null && isDefault != null) {
isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
Expand Down Expand Up @@ -553,7 +560,9 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
}

for (BuilderFieldData bfd : job.builderFields) {
makePrefixedSetterMethodsForBuilder(job, bfd, annInstance.setterPrefix());
if (!bfd.toExclude) {
makePrefixedSetterMethodsForBuilder(job, bfd, annInstance.setterPrefix());
}
}

{
Expand Down
26 changes: 26 additions & 0 deletions src/core/lombok/eclipse/handlers/HandleBuilderExclude.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package lombok.eclipse.handlers;

import lombok.Builder;
import lombok.core.AST;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.experimental.SuperBuilder;
import lombok.spi.Provides;
import org.eclipse.jdt.internal.compiler.ast.Annotation;

@Provides
@HandlerPriority(-1025) //HandleBuilder's level, minus one.
public class HandleBuilderExclude extends EclipseAnnotationHandler<Builder.Exclude> {
@Override
public void handle(AnnotationValues<Builder.Exclude> annotation, Annotation ast, EclipseNode annotationNode) {
EclipseNode annotatedField = annotationNode.up();
if (annotatedField.getKind() != AST.Kind.FIELD) return;
EclipseNode classWithAnnotatedField = annotatedField.up();
if (!EclipseHandlerUtil.hasAnnotation(Builder.class, classWithAnnotatedField) && !EclipseHandlerUtil.hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)
&& !EclipseHandlerUtil.hasAnnotation(SuperBuilder.class, classWithAnnotatedField)) {
annotationNode.addWarning("@Builder.Exclude requires @Builder or @SuperBuilder on the class for it to mean anything.");
}
}
}
11 changes: 10 additions & 1 deletion src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.findAnnotation;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
Expand All @@ -33,6 +34,7 @@
import java.util.HashSet;
import java.util.List;

import lombok.javac.JavacNode;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
Expand Down Expand Up @@ -198,6 +200,7 @@ void setBuilderToAbstract() {

boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent));
for (EclipseNode fieldNode : HandleConstructor.findAllFields(parent, true)) {

FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode);
boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));
Expand All @@ -212,6 +215,10 @@ void setBuilderToAbstract() {
bfd.type = fd.type;
bfd.singularData = getSingularData(fieldNode, ast, annInstance.setterPrefix());
bfd.originalFieldNode = fieldNode;
EclipseNode isExcluded = findAnnotation(Builder.Exclude.class, fieldNode);
if (isExcluded != null) {
bfd.toExclude = true;
}

if (bfd.singularData != null && isDefault != null) {
isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
Expand Down Expand Up @@ -389,7 +396,9 @@ void setBuilderToAbstract() {

// Create the setter methods in the abstract builder.
for (BuilderFieldData bfd : job.builderFields) {
generateSetterMethodsForBuilder(job, bfd, builderGenericName, annInstance.setterPrefix());
if (!bfd.toExclude) {
generateSetterMethodsForBuilder(job, bfd, builderGenericName, annInstance.setterPrefix());
}
}

// Generate abstract self() and build() methods in the abstract builder.
Expand Down
9 changes: 8 additions & 1 deletion src/core/lombok/javac/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ static class BuilderFieldData {
ObtainVia obtainVia;
JavacNode obtainViaNode;
JavacNode originalFieldNode;
boolean toExclude = false;

java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
}
Expand Down Expand Up @@ -264,6 +265,10 @@ static class BuilderFieldData {
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode, annInstance.setterPrefix());
bfd.originalFieldNode = fieldNode;
JavacNode isExcluded = findAnnotation(Builder.Exclude.class, fieldNode, false);
if (isExcluded != null) {
bfd.toExclude = true;
}

if (bfd.singularData != null && isDefault != null) {
isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
Expand Down Expand Up @@ -489,7 +494,9 @@ static class BuilderFieldData {
}

for (BuilderFieldData bfd : job.builderFields) {
makePrefixedSetterMethodsForBuilder(job, bfd, annInstance.setterPrefix());
if (!bfd.toExclude) {
makePrefixedSetterMethodsForBuilder(job, bfd, annInstance.setterPrefix());
}
}

{
Expand Down
29 changes: 29 additions & 0 deletions src/core/lombok/javac/handlers/HandleBuilderExclude.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package lombok.javac.handlers;

import com.sun.tools.javac.tree.JCTree;
import lombok.Builder;
import lombok.core.AST;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.SuperBuilder;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.spi.Provides;

import static lombok.javac.handlers.JavacHandlerUtil.deleteAnnotationIfNeccessary;
import static lombok.javac.handlers.JavacHandlerUtil.hasAnnotation;

@Provides
@HandlerPriority(-1026) //HandleBuilder's level, minus one.
public class HandleBuilderExclude extends JavacAnnotationHandler<Builder.Exclude> {
@Override public void handle(AnnotationValues<Builder.Exclude> annotation, JCTree.JCAnnotation ast, JavacNode annotationNode) {
JavacNode annotatedField = annotationNode.up();
if (annotatedField.getKind() != AST.Kind.FIELD) return;
JavacNode classWithAnnotatedField = annotatedField.up();
if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)
&& !hasAnnotation(SuperBuilder.class, classWithAnnotatedField)) {
annotationNode.addWarning("@Builder.Exclude requires @Builder or @SuperBuilder on the class for it to mean anything.");
deleteAnnotationIfNeccessary(annotationNode, Builder.Exclude.class);
}
}
}
25 changes: 25 additions & 0 deletions src/core/lombok/javac/handlers/HandleBuilderExcludeRemove.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package lombok.javac.handlers;

import static lombok.javac.handlers.JavacHandlerUtil.*;

import com.sun.tools.javac.tree.JCTree.JCAnnotation;

import lombok.Builder;
import lombok.Builder.Exclude;
import lombok.core.AlreadyHandledAnnotations;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.spi.Provides;

@Provides
@HandlerPriority(32768)
@AlreadyHandledAnnotations
public class HandleBuilderExcludeRemove extends JavacAnnotationHandler<Builder.Exclude> {
@Override public void handle(AnnotationValues<Exclude> annotation, JCAnnotation ast, JavacNode annotationNode) {
deleteAnnotationIfNeccessary(annotationNode, Builder.Exclude.class);
deleteImportFromCompilationUnit(annotationNode, Builder.class.getName());
deleteImportFromCompilationUnit(annotationNode, Builder.Exclude.class.getName());
}
}
8 changes: 7 additions & 1 deletion src/core/lombok/javac/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode, annInstance.setterPrefix());
bfd.originalFieldNode = fieldNode;
JavacNode isExcluded = findAnnotation(Builder.Exclude.class, fieldNode, false);
if (isExcluded != null) {
bfd.toExclude = true;
}

if (bfd.singularData != null && isDefault != null) {
isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
Expand Down Expand Up @@ -341,7 +345,9 @@ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast,

// Create the setter methods in the abstract builder.
for (BuilderFieldData bfd : job.builderFields) {
generateSetterMethodsForBuilder(job, bfd, builderGenericName, annInstance.setterPrefix());
if (!bfd.toExclude) {
generateSetterMethodsForBuilder(job, bfd, builderGenericName, annInstance.setterPrefix());
}
}

// Generate abstract self() and build() methods in the abstract builder.
Expand Down
72 changes: 72 additions & 0 deletions test/transform/resource/after-delombok/BuilderExcludes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
public class BuilderExcludes {
long x;
int y;
int z;

@java.lang.SuppressWarnings("all")
@lombok.Generated
BuilderExcludes(final long x, final int y, final int z) {
this.x = x;
this.y = y;
this.z = z;
}


@java.lang.SuppressWarnings("all")
@lombok.Generated
public static class BuilderExcludesBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private long x;
@java.lang.SuppressWarnings("all")
@lombok.Generated
private int y;
@java.lang.SuppressWarnings("all")
@lombok.Generated
private int z;

@java.lang.SuppressWarnings("all")
@lombok.Generated
BuilderExcludesBuilder() {
}

/**
* @return {@code this}.
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
public BuilderExcludes.BuilderExcludesBuilder x(final long x) {
this.x = x;
return this;
}

/**
* @return {@code this}.
*/
@java.lang.SuppressWarnings("all")
@lombok.Generated
public BuilderExcludes.BuilderExcludesBuilder y(final int y) {
this.y = y;
return this;
}

@java.lang.SuppressWarnings("all")
@lombok.Generated
public BuilderExcludes build() {
return new BuilderExcludes(this.x, this.y, this.z);
}

@java.lang.Override
@java.lang.SuppressWarnings("all")
@lombok.Generated
public java.lang.String toString() {
return "BuilderExcludes.BuilderExcludesBuilder(x=" + this.x + ", y=" + this.y + ", z=" + this.z + ")";
}
}

@java.lang.SuppressWarnings("all")
@lombok.Generated
public static BuilderExcludes.BuilderExcludesBuilder builder() {
return new BuilderExcludes.BuilderExcludesBuilder();
}
}
Loading