Skip to content

Commit

Permalink
[fixes #2083] adds feature ‘access level’ to @Builder
Browse files Browse the repository at this point in the history
  • Loading branch information
rzwitserloot committed Apr 30, 2019
1 parent ec04d9d commit a318029
Show file tree
Hide file tree
Showing 19 changed files with 201 additions and 167 deletions.
1 change: 1 addition & 0 deletions doc/changelog.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Lombok Changelog

### v1.18.7 "Edgy Guinea Pig"
* FEATURE: You can now suppress generation of the `builder` method when using `@Builder`; usually because you're only interested in the `toBuilder` method. As a convenience we won't emit warnings about missing `@Builder.Default` annotations when you do this. [Issue #2046](https://github.com/rzwitserloot/lombok/issues/2046)
* FEATURE: You can now change the access modifier of generated builder classes. [Issue #2083](https://github.com/rzwitserloot/lombok/issues/2083).
* FEATURE: When using `@NonNull`, or any other annotation that would result in a null-check, you can configure to generate an assert statement instead. [Issue #2078](https://github.com/rzwitserloot/lombok/issues/2078).
* PLATFORM: A few lombok features (most notably delombok) failed on JDK12. [Issue #2082](https://github.com/rzwitserloot/lombok/issues/2082)
* BUGFIX: var/val on methods that return an intersection type would now work in Eclipse. [Issue #1986](https://github.com/rzwitserloot/lombok/issues/1986)
Expand Down
8 changes: 8 additions & 0 deletions src/core/lombok/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@
*/
boolean toBuilder() default false;

/**
* Sets the access level of the generated builder class. By default, generated builder classes are {@code public}.
* Note: This does nothing if you write your own builder class (we won't change its access level).
*
* @return The builder class will be generated with this access modifier.
*/
AccessLevel access() default lombok.AccessLevel.PUBLIC;

/**
* Put on a field (in case of {@code @Builder} on a type) or a parameter (for {@code @Builder} on a constructor or static method) to
* indicate how lombok should obtain a value for this field or parameter given an instance; this is only relevant if {@code toBuilder} is {@code true}.
Expand Down
9 changes: 5 additions & 4 deletions src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2018 The Project Lombok Authors.
* Copyright (C) 2015-2019 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
Expand Down Expand Up @@ -58,6 +58,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;

import lombok.AccessLevel;
import lombok.core.LombokImmutableList;
import lombok.core.SpiLoadUtil;
import lombok.core.TypeLibrary;
Expand Down Expand Up @@ -234,7 +235,7 @@ public List<char[]> listMethodsToBeGenerated(SingularData data, EclipseNode buil
* If you need more control over the return type and value, use
* {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, TypeReferenceMaker, StatementMaker)}.
*/
public void generateMethods(SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain) {
public void generateMethods(SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain, AccessLevel access) {
TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
@Override public TypeReference make() {
return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
Expand All @@ -247,14 +248,14 @@ public void generateMethods(SingularData data, boolean deprecate, final EclipseN
}
};

generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker, access);
}

/**
* Generates the singular, plural, and clear methods for the given {@link SingularData}.
* Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods.
*/
public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker);
public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access);

public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable);

Expand Down
43 changes: 25 additions & 18 deletions src/core/lombok/eclipse/handlers/HandleBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;

Builder builderInstance = annotation.getInstance();
AccessLevel accessForOuters = builderInstance.access();
if (accessForOuters == null) accessForOuters = AccessLevel.PUBLIC;
if (builderInstance.access() == AccessLevel.NONE) {
annotationNode.addError("AccessLevel.NONE is not valid here");
accessForOuters = AccessLevel.PUBLIC;
}
AccessLevel accessForInners = accessForOuters == AccessLevel.PROTECTED ? AccessLevel.PUBLIC : accessForOuters;

// These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them.
boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true);
Expand Down Expand Up @@ -408,7 +415,7 @@ private static final char[] prefixWith(char[] prefix, char[] name) {

EclipseNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast);
builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, accessForOuters);
} else {
TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get();
if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) {
Expand Down Expand Up @@ -469,14 +476,14 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
}

for (BuilderFieldData bfd : builderFields) {
makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain, accessForInners);
}

{
MemberExistsResult methodExists = methodExists(buildMethodName, builderType, -1);
if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(buildMethodName, builderType, 0);
if (methodExists == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, accessForInners);
if (md != null) injectMethod(builderType, md);
}
}
Expand All @@ -499,7 +506,7 @@ private static final char[] prefixWith(char[] prefix, char[] name) {

if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false;
if (generateBuilderMethod) {
MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast);
MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast, accessForOuters);
if (md != null) injectMethod(tdParent, md);
}

Expand All @@ -516,7 +523,7 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
tps[i].name = typeArgsForToBuilder.get(i);
}
}
MethodDeclaration md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
MethodDeclaration md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters);

if (md != null) injectMethod(tdParent, md);
}
Expand All @@ -529,13 +536,13 @@ private static final char[] prefixWith(char[] prefix, char[] name) {
}

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) {
private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source, AccessLevel access) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;

MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.selector = methodName.toCharArray();
out.modifiers = ClassFileConstants.AccPublic;
out.modifiers = toEclipseModifier(access);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
AllocationExpression invoke = new AllocationExpression();
Expand Down Expand Up @@ -632,7 +639,7 @@ private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFiel
return decl;
}

public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, AccessLevel access) {
MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
List<Statement> statements = new ArrayList<Statement>();
Expand Down Expand Up @@ -677,7 +684,7 @@ public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isSta
statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0));
}

out.modifiers = ClassFileConstants.AccPublic;
out.modifiers = toEclipseModifier(access);
out.selector = name.toCharArray();
out.thrownExceptions = copyTypes(thrownExceptions);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
Expand Down Expand Up @@ -736,13 +743,13 @@ public static MethodDeclaration generateDefaultProvider(char[] methodName, TypeP
return out;
}

public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source, AccessLevel access) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;

MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.selector = builderMethodName.toCharArray();
out.modifiers = ClassFileConstants.AccPublic;
out.modifiers = toEclipseModifier(access);
if (isStatic) out.modifiers |= ClassFileConstants.AccStatic;
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
Expand Down Expand Up @@ -795,16 +802,16 @@ public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData

private static final AbstractMethodDeclaration[] EMPTY = {};

public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain) {
public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access) {
boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);
if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations);
makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access);
} else {
bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain);
bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain, access);
}
}

private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations) {
private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY;
Expand All @@ -820,16 +827,16 @@ private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean d

String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());

MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, chain, ClassFileConstants.AccPublic,
MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, chain, toEclipseModifier(access),
sourceNode, Collections.<Annotation>emptyList(), annotations != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annotations)) : Collections.<Annotation>emptyList());
injectMethod(builderType, setter);
}

public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) {
public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source, AccessLevel access) {
TypeDeclaration parent = (TypeDeclaration) tdParent.get();
TypeDeclaration builder = new TypeDeclaration(parent.compilationResult);
builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
builder.modifiers |= ClassFileConstants.AccPublic;
builder.modifiers |= toEclipseModifier(access);
if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic;
builder.typeParameters = copyTypeParams(typeParams, source);
builder.name = builderClassName.toCharArray();
Expand Down
2 changes: 1 addition & 1 deletion src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ private void generateSetterMethodsForBuilder(EclipseNode builderType, BuilderFie
if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations);
} else {
bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker);
bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2018 The Project Lombok Authors.
* Copyright (C) 2015-2019 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
Expand Down Expand Up @@ -28,6 +28,7 @@
import java.util.Collections;
import java.util.List;

import lombok.AccessLevel;
import lombok.core.GuavaTypeMap;
import lombok.core.LombokImmutableList;
import lombok.core.handlers.HandlerUtil;
Expand Down Expand Up @@ -99,16 +100,16 @@ protected char[][] makeGuavaTypeName(String simpleName, boolean addBuilder) {
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}

@Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType);
@Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker, AccessLevel access) {
generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access);
generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent, access);
generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, access);
}

void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, AccessLevel access) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
md.modifiers = toEclipseModifier(access);

FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
thisDotField.receiver = new ThisReference(0, 0);
Expand All @@ -122,7 +123,7 @@ void generateClearMethod(boolean deprecate, TypeReference returnType, Statement
injectMethod(builderType, md);
}

void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) {
LombokImmutableList<String> suffixes = getArgumentSuffixes();
char[][] names = new char[suffixes.size()][];
for (int i = 0; i < suffixes.size(); i++) {
Expand All @@ -133,7 +134,7 @@ void generateSingularMethod(boolean deprecate, TypeReference returnType, Stateme

MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
md.modifiers = toEclipseModifier(access);

List<Statement> statements = new ArrayList<Statement>();
statements.add(createConstructBuilderVarIfNeeded(data, builderType));
Expand Down Expand Up @@ -166,10 +167,10 @@ void generateSingularMethod(boolean deprecate, TypeReference returnType, Stateme
HandleNonNull.INSTANCE.fix(injectMethod(builderType, md));
}

void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent, AccessLevel access) {
MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;
md.modifiers = toEclipseModifier(access);

List<Statement> statements = new ArrayList<Statement>();
statements.add(createConstructBuilderVarIfNeeded(data, builderType));
Expand Down
Loading

0 comments on commit a318029

Please sign in to comment.