aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--src/core/lombok/Builder.java10
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleBuilder.java71
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java60
-rw-r--r--test/transform/resource/after-delombok/BuilderWithPrefix.java34
-rw-r--r--test/transform/resource/after-ecj/BuilderWithPrefix.java27
-rw-r--r--test/transform/resource/before/BuilderWithPrefix.java6
7 files changed, 191 insertions, 20 deletions
diff --git a/.gitignore b/.gitignore
index b3f7b18d..824ae9c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,10 +15,11 @@
/.factorypath
/lombok.iml
/.idea
+*.iml
*.markdown.html
/junit*.properties
/eclipse.location
/.apt_generated/
/out
/website/lombokSupporters
-/pom.xml \ No newline at end of file
+/pom.xml
diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java
index dfa5ecb5..d7fe42a1 100644
--- a/src/core/lombok/Builder.java
+++ b/src/core/lombok/Builder.java
@@ -153,6 +153,16 @@ public @interface Builder {
* @return The builder class will be generated with this access modifier.
*/
AccessLevel access() default lombok.AccessLevel.PUBLIC;
+
+ /**
+ * Prefix to prepend to set methods in the generated builder class. By default, generated methods to not include a
+ * prefix. If this value populated, the first letter of the generated method name will be capitalized.
+ *
+ * For example, a method normally generated as {@code someField(String someField)} would instead be generated as {@code withSomeField(String someField)}
+ *
+ * @return The prefix to prepend to generated method names.
+ */
+ String setterPrefix() default "";
/**
* 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
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 8bfdeb65..375bd164 100755
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -31,6 +31,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import com.sun.tools.javac.util.Name;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
@@ -160,7 +161,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
@Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode);
-
+
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
Builder builderInstance = annotation.getInstance();
@@ -492,7 +493,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
for (BuilderFieldData bfd : builderFields) {
- makeSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners, bfd.originalFieldNode);
+ makePrefixedSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners, bfd.originalFieldNode, builderInstance.setterPrefix());
}
{
@@ -631,7 +632,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (cfv.generateUnique()) {
out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)};
}
-
+
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
@@ -661,12 +662,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
static Argument[] generateBuildArgs(CheckerFrameworkVersion cfv, EclipseNode type, List<BuilderFieldData> builderFields, ASTNode source) {
if (!cfv.generateCalledMethods()) return null;
-
+
List<char[]> mandatories = new ArrayList<char[]>();
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData == null && bfd.nameOfSetFlag == null) mandatories.add(bfd.name);
}
-
+
if (mandatories.size() == 0) return null;
char[][] nameCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__CALLED);
SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameCalled, poss(source, nameCalled.length)), source.sourceStart);
@@ -686,7 +687,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
arg.annotations = new Annotation[] {ann};
return new Argument[] {arg};
}
-
+
public MethodDeclaration generateBuildMethod(CheckerFrameworkVersion cfv, 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;
@@ -889,6 +890,59 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
String setterName = fluent ? new String(paramName) : HandlerUtil.buildAccessorName("set", new String(paramName));
List<Annotation> methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode));
+ Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode);
+ if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns);
+ MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, chain, toEclipseModifier(access),
+ sourceNode, methodAnnsList, annotations != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annotations)) : Collections.<Annotation>emptyList());
+ if (cfv.generateCalledMethods()) {
+ Argument[] arr = setter.arguments == null ? new Argument[0] : setter.arguments;
+ Argument[] newArr = new Argument[arr.length + 1];
+ System.arraycopy(arr, 0, newArr, 1, arr.length);
+ newArr[0] = new Argument(new char[] { 't', 'h', 'i', 's' }, 0, new SingleTypeReference(builderType.getName().toCharArray(), 0), Modifier.FINAL);
+ char[][] nameNotCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__NOT_CALLED);
+ SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(source, nameNotCalled.length)), source.sourceStart);
+ ann.memberValue = new StringLiteral(setterName.toCharArray(), 0, 0, 0);
+ newArr[0].annotations = new Annotation[] {ann};
+ setter.arguments = newArr;
+ }
+ injectMethod(builderType, setter);
+ }
+
+ public void makePrefixedSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access, EclipseNode originalFieldNode, String prefix) {
+ boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);
+ if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
+ makePrefixedSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access, originalFieldNode, prefix);
+ } else {
+ bfd.singularData.getSingularizer().generateMethods(cfv, bfd.singularData, deprecate, builderType, fluent, chain, access);
+ }
+ }
+
+ private void makePrefixedSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access, EclipseNode originalFieldNode, String prefix) {
+ TypeDeclaration td = (TypeDeclaration) builderType.get();
+ AbstractMethodDeclaration[] existing = td.methods;
+ if (existing == null) existing = EMPTY;
+ int len = existing.length;
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ char[] name = fd.name;
+
+ for (int i = 0; i < len; i++) {
+ if (!(existing[i] instanceof MethodDeclaration)) continue;
+ char[] existingName = existing[i].selector;
+ if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return;
+ }
+
+ String setterPrefix = prefix.isEmpty() ? "set" : prefix;
+ String setterName;
+ if(fluent) {
+ setterName = prefix.isEmpty() ? new String(paramName) : HandlerUtil.buildAccessorName(setterPrefix, new String(paramName));
+ } else {
+ setterName = HandlerUtil.buildAccessorName(setterPrefix, new String(paramName));
+ }
+
+ List<Annotation> methodAnnsList = Collections.<Annotation>emptyList();
+ Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode);
+ if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns);
+ ASTNode source = sourceNode.get();
MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, chain, toEclipseModifier(access),
sourceNode, methodAnnsList, annotations != null ? Arrays.asList(copyAnnotations(source, annotations)) : Collections.<Annotation>emptyList());
if (cfv.generateCalledMethods()) {
@@ -897,14 +951,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
System.arraycopy(arr, 0, newArr, 1, arr.length);
newArr[0] = new Argument(new char[] { 't', 'h', 'i', 's' }, 0, new SingleTypeReference(builderType.getName().toCharArray(), 0), Modifier.FINAL);
char[][] nameNotCalled = fromQualifiedName(CheckerFrameworkVersion.NAME__NOT_CALLED);
- SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(source, nameNotCalled.length)), source.sourceStart);
+ SingleMemberAnnotation ann = new SingleMemberAnnotation(new QualifiedTypeReference(nameNotCalled, poss(
+ source, nameNotCalled.length)), source.sourceStart);
ann.memberValue = new StringLiteral(setterName.toCharArray(), 0, 0, 0);
newArr[0].annotations = new Annotation[] {ann};
setter.arguments = newArr;
}
injectMethod(builderType, setter);
}
-
+
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);
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index de2bdde1..da40692e 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -104,7 +104,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
@Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
CheckerFrameworkVersion cfv = getCheckerFrameworkVersion(annotationNode);
-
+
Builder builderInstance = annotation.getInstance();
AccessLevel accessForOuters = builderInstance.access();
if (accessForOuters == null) accessForOuters = AccessLevel.PUBLIC;
@@ -436,7 +436,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
for (BuilderFieldData bfd : builderFields) {
- makeSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners);
+ makePrefixedSetterMethodsForBuilder(cfv, builderType, bfd, annotationNode, fluent, chain, accessForInners, builderInstance.setterPrefix());
}
{
@@ -622,12 +622,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
static List<JCVariableDecl> generateBuildArgs(CheckerFrameworkVersion cfv, JavacNode type, java.util.List<BuilderFieldData> builderFields) {
if (!cfv.generateCalledMethods()) return List.<JCVariableDecl>nil();
-
+
ArrayList<String> mandatories = new ArrayList<String>();
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData == null && bfd.nameOfSetFlag == null) mandatories.add(bfd.name.toString());
}
-
+
JCExpression arg;
JavacTreeMaker maker = type.getTreeMaker();
if (mandatories.size() == 0) return List.<JCVariableDecl>nil();
@@ -642,7 +642,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCVariableDecl recv = maker.VarDef(maker.Modifiers(0L, List.<JCAnnotation>of(recvAnno)), type.toName("this"), maker.Ident(builderTypeNode.name), null);
return List.of(recv);
}
-
+
private JCMethodDecl generateBuildMethod(CheckerFrameworkVersion cfv, JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning, AccessLevel access) {
JavacTreeMaker maker = type.getTreeMaker();
@@ -787,18 +787,56 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
private void makeSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode, AccessLevel access) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
-
+
for (JavacNode child : builderType.down()) {
if (child.getKind() != Kind.METHOD) continue;
JCMethodDecl methodDecl = (JCMethodDecl) child.get();
Name existingName = methodDecl.name;
if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return;
}
-
+
String setterName = fluent ? paramName.toString() : HandlerUtil.buildAccessorName("set", paramName.toString());
-
+
JavacTreeMaker maker = fieldNode.getTreeMaker();
-
+
+ List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode);
+ JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, chain, source, methodAnns, annosOnParam);
+ recursiveSetGeneratedBy(newMethod, source.get(), builderType.getContext());
+ copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER);
+
+ injectMethod(builderType, newMethod);
+ }
+
+ public void makePrefixedSetterMethodsForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain, AccessLevel access, String prefix) {
+ boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode);
+ if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
+ makePrefixedSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, fluent, chain, fieldNode.annotations, fieldNode.originalFieldNode, access, prefix);
+ } else {
+ // TODO prefixed version
+ fieldNode.singularData.getSingularizer().generateMethods(cfv, fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain, access);
+ }
+ }
+
+ private void makePrefixedSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode, AccessLevel access, String prefix) {
+ Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
+
+ for (JavacNode child : builderType.down()) {
+ if (child.getKind() != Kind.METHOD) continue;
+ JCMethodDecl methodDecl = (JCMethodDecl) child.get();
+ Name existingName = methodDecl.name;
+ if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return;
+ }
+
+ String setterPrefix = prefix.isEmpty() ? "set" : prefix;
+ String setterName;
+ if(fluent) {
+ setterName = prefix.isEmpty() ? paramName.toString() : HandlerUtil.buildAccessorName(setterPrefix, paramName.toString());
+ } else {
+ setterName = HandlerUtil.buildAccessorName(setterPrefix, paramName.toString());
+ }
+
+ JavacTreeMaker maker = fieldNode.getTreeMaker();
+
List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode);
JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, chain, source, methodAnns, annosOnParam);
if (cfv.generateCalledMethods()) {
@@ -809,10 +847,10 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
recursiveSetGeneratedBy(newMethod, source.get(), builderType.getContext());
copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER);
-
+
injectMethod(builderType, newMethod);
}
-
+
public JavacNode makeBuilderClass(boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast, AccessLevel access) {
JavacTreeMaker maker = tdParent.getTreeMaker();
int modifiers = toJavacModifier(access);
diff --git a/test/transform/resource/after-delombok/BuilderWithPrefix.java b/test/transform/resource/after-delombok/BuilderWithPrefix.java
new file mode 100644
index 00000000..c29d2a16
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderWithPrefix.java
@@ -0,0 +1,34 @@
+import java.util.List;
+class BuilderWithPrefix<T> {
+ private int unprefixed;
+ @java.lang.SuppressWarnings("all")
+ BuilderWithPrefix(final int unprefixed) {
+ this.unprefixed = unprefixed;
+ }
+ @java.lang.SuppressWarnings("all")
+ protected static class BuilderWithPrefixBuilder<T> {
+ @java.lang.SuppressWarnings("all")
+ private int unprefixed;
+ @java.lang.SuppressWarnings("all")
+ BuilderWithPrefixBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderWithPrefixBuilder<T> withUnprefixed(final int unprefixed) {
+ this.unprefixed = unprefixed;
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderWithPrefix<T> build() {
+ return new BuilderWithPrefix<T>(unprefixed);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderWithPrefix.BuilderWithPrefixBuilder(unprefixed=" + this.unprefixed + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected static <T> BuilderWithPrefixBuilder<T> builder() {
+ return new BuilderWithPrefixBuilder<T>();
+ }
+}
diff --git a/test/transform/resource/after-ecj/BuilderWithPrefix.java b/test/transform/resource/after-ecj/BuilderWithPrefix.java
new file mode 100644
index 00000000..98c42fe9
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderWithPrefix.java
@@ -0,0 +1,27 @@
+import java.util.List;
+@lombok.Builder(access = lombok.AccessLevel.PROTECTED,setterPrefix = "with") class BuilderWithPrefix<T> {
+ protected static @java.lang.SuppressWarnings("all") class BuilderWithPrefixBuilder<T> {
+ private @java.lang.SuppressWarnings("all") int unprefixed;
+ @java.lang.SuppressWarnings("all") BuilderWithPrefixBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderWithPrefixBuilder<T> withUnprefixed(final int unprefixed) {
+ this.unprefixed = unprefixed;
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderWithPrefix<T> build() {
+ return new BuilderWithPrefix<T>(unprefixed);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("BuilderWithPrefix.BuilderWithPrefixBuilder(unprefixed=" + this.unprefixed) + ")");
+ }
+ }
+ private int unprefixed;
+ @java.lang.SuppressWarnings("all") BuilderWithPrefix(final int unprefixed) {
+ super();
+ this.unprefixed = unprefixed;
+ }
+ protected static @java.lang.SuppressWarnings("all") <T>BuilderWithPrefixBuilder<T> builder() {
+ return new BuilderWithPrefixBuilder<T>();
+ }
+}
diff --git a/test/transform/resource/before/BuilderWithPrefix.java b/test/transform/resource/before/BuilderWithPrefix.java
new file mode 100644
index 00000000..38f3c029
--- /dev/null
+++ b/test/transform/resource/before/BuilderWithPrefix.java
@@ -0,0 +1,6 @@
+import java.util.List;
+
+@lombok.Builder(access = lombok.AccessLevel.PROTECTED, setterPrefix = "with")
+class BuilderWithPrefix<T> {
+ private int unprefixed;
+}