From 88bf742e3e107cc0bbfc3a72c2f456d34ef3079c Mon Sep 17 00:00:00 2001 From: Caleb Brinkman Date: Wed, 10 Jul 2019 17:27:01 -0500 Subject: Implement prefixed setters Related to #1805, this change adds an optional `setterPrefix` parameter to the `Builder` annotation; if this parameter is unspecified or blank the behavior of the `Builder` annotation is unchanged, but if it is present the value specified will be prefixed to the generated methods. For example, using: ``` @Builder(setterPrefix = "include") class Foo { private int someValue; } ``` will result in a generated `Builder` class containing an `includeSomeValue(int someValue)` method instead of the default `someValue(int someValue)`. --- src/core/lombok/javac/handlers/HandleBuilder.java | 54 +++++++++++++++++++---- 1 file changed, 46 insertions(+), 8 deletions(-) (limited to 'src/core/lombok/javac/handlers/HandleBuilder.java') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index b339c2ca..ab63aa5a 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -430,7 +430,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } for (BuilderFieldData bfd : builderFields) { - makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain, accessForInners); + makePrefixedSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain, accessForInners, builderInstance.setterPrefix()); } { @@ -745,29 +745,67 @@ public class HandleBuilder extends JavacAnnotationHandler { fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain, access); } } - + private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List 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 ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); - + JavacTreeMaker maker = fieldNode.getTreeMaker(); - + List methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, methodAnns, annosOnParam); recursiveSetGeneratedBy(newMethod, source.get(), builderType.getContext()); copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER); - + injectMethod(builderType, newMethod); } - + + public void makePrefixedSetterMethodsForBuilder(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(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, fluent, chain, fieldNode.annotations, fieldNode.originalFieldNode, access, prefix); + } else { + // TODO prefixed version + fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain, access); + } + } + + private void makePrefixedSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List 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() ? fieldNode.getName() : HandlerUtil.buildAccessorName(setterPrefix, fieldNode.getName()); + } else { + setterName = HandlerUtil.buildAccessorName(setterPrefix, fieldNode.getName()); + } + + JavacTreeMaker maker = fieldNode.getTreeMaker(); + + List methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); + JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, methodAnns, annosOnParam); + 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 typeParams, JCAnnotation ast, AccessLevel access) { JavacTreeMaker maker = tdParent.getTreeMaker(); int modifiers = toJavacModifier(access); -- cgit From e1bd41fab9d62b5478e50b5c2fe2e45a0d767334 Mon Sep 17 00:00:00 2001 From: abrinkman94 Date: Thu, 19 Sep 2019 11:30:12 -0500 Subject: Added setterPrefix to javac --- src/core/lombok/javac/handlers/HandleBuilder.java | 9 ++++---- .../javac/handlers/JavacSingularsRecipes.java | 26 +++++++++++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) (limited to 'src/core/lombok/javac/handlers/HandleBuilder.java') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index da40692e..f199f161 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -180,7 +180,7 @@ public class HandleBuilder extends JavacAnnotationHandler { bfd.builderFieldName = bfd.name; bfd.annotations = findCopyableAnnotations(fieldNode); bfd.type = fd.vartype; - bfd.singularData = getSingularData(fieldNode); + bfd.singularData = getSingularData(fieldNode, builderInstance.setterPrefix()); bfd.originalFieldNode = fieldNode; if (bfd.singularData != null && isDefault != null) { @@ -369,7 +369,7 @@ public class HandleBuilder extends JavacAnnotationHandler { bfd.rawName = raw.name; bfd.annotations = findCopyableAnnotations(param); bfd.type = raw.vartype; - bfd.singularData = getSingularData(param); + bfd.singularData = getSingularData(param, builderInstance.setterPrefix()); bfd.originalFieldNode = param; addObtainVia(bfd, param); builderFields.add(bfd); @@ -876,8 +876,9 @@ public class HandleBuilder extends JavacAnnotationHandler { * or parameter), or null if there's no {@code @Singular} annotation on it. * * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation. + * @param setterPrefix */ - private SingularData getSingularData(JavacNode node) { + private SingularData getSingularData(JavacNode node, final String setterPrefix) { for (JavacNode child : node.down()) { if (!annotationTypeMatches(Singular.class, child)) continue; Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; @@ -919,7 +920,7 @@ public class HandleBuilder extends JavacAnnotationHandler { return null; } - return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer); + return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer, setterPrefix); } return null; diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 341d44df..f693af83 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -117,6 +117,7 @@ public class JavacSingularsRecipes { private final List typeArgs; private final String targetFqn; private final JavacSingularizer singularizer; + private final String setterPrefix; public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer) { this.annotation = annotation; @@ -125,6 +126,17 @@ public class JavacSingularsRecipes { this.typeArgs = typeArgs; this.targetFqn = targetFqn; this.singularizer = singularizer; + this.setterPrefix = null; + } + + public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer, String setterPrefix) { + this.annotation = annotation; + this.singularName = singularName; + this.pluralName = pluralName; + this.typeArgs = typeArgs; + this.targetFqn = targetFqn; + this.singularizer = singularizer; + this.setterPrefix = setterPrefix; } public JavacNode getAnnotation() { @@ -138,6 +150,8 @@ public class JavacSingularsRecipes { public Name getPluralName() { return pluralName; } + + public String getSetterPrefix() { return setterPrefix; } public List getTypeArgs() { return typeArgs; @@ -279,8 +293,12 @@ public class JavacSingularsRecipes { ListBuffer statements = generateSingularMethodStatements(maker, data, builderType, source); List params = generateSingularMethodParameters(maker, data, builderType, source); Name name = data.getSingularName(); - if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName(), name.toString())); - + Name prefixedSingularName = data.getSetterPrefix().length() == 0 ? name : + builderType.toName(HandlerUtil.buildAccessorName(data.getSetterPrefix(), name.toString())); + + name = fluent ? prefixedSingularName : builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName(), + name.toString())); + statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access); } @@ -307,7 +325,9 @@ public class JavacSingularsRecipes { private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { ListBuffer statements = generatePluralMethodStatements(maker, data, builderType, source); Name name = data.getPluralName(); - if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName() + "All", name.toString())); + Name prefixedSingularName = builderType.toName(data.getSetterPrefix()); + name = fluent ? prefixedSingularName : builderType.toName(HandlerUtil.buildAccessorName( + getAddMethodName() + "All", name.toString())); JCExpression paramType = getPluralMethodParamType(builderType); paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs(), source); long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); -- cgit From 2a7afbc1d1f450dec98b224ffd990a7a972d770a Mon Sep 17 00:00:00 2001 From: Caleb Brinkman Date: Mon, 28 Oct 2019 12:43:43 -0500 Subject: Fix more singulars and tests --- src/core/lombok/eclipse/handlers/HandleBuilder.java | 1 - .../eclipse/handlers/singulars/EclipseGuavaSingularizer.java | 6 ++++-- src/core/lombok/javac/handlers/HandleBuilder.java | 12 +++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'src/core/lombok/javac/handlers/HandleBuilder.java') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 63eb60a3..aab97e18 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -568,7 +568,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { List statements = null; for (BuilderFieldData bfd : builderFields) { String setterPrefix = prefix.isEmpty() ? "set" : prefix; - //char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray(); String setterName; if(fluent) { setterName = prefix.isEmpty() ? new String(bfd.name) : HandlerUtil.buildAccessorName(setterPrefix, new String(bfd.name)); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 338f5eab..8f80d228 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -172,7 +172,8 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.arguments[i].annotations = typeUseAnns; } md.returnType = returnType; - md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(getAddMethodName(), new String(data.getSingularName())).toCharArray(); + char[] prefixedSingularName = data.getSetterPrefix().length == 0 ? data.getSingularName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getSingularName())).toCharArray(); + md.selector = fluent ? prefixedSingularName : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray(); md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); @@ -204,7 +205,8 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal); md.arguments = new Argument[] {param}; md.returnType = returnType; - md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(getAddMethodName() + "All", new String(data.getPluralName())).toCharArray(); + char[] prefixedSelector = data.getSetterPrefix().length == 0 ? data.getPluralName() : HandlerUtil.buildAccessorName(new String(data.getSetterPrefix()), new String(data.getPluralName())).toCharArray(); + md.selector = fluent ? prefixedSelector : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray(); md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index f199f161..a7235a40 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -487,7 +487,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } tps = lb.toList(); } - JCMethodDecl md = generateToBuilderMethod(cfv, toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters); + JCMethodDecl md = generateToBuilderMethod(cfv, toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters, builderInstance.setterPrefix()); if (md != null) { recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); injectMethod(tdParent, md); @@ -536,7 +536,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } private static final String BUILDER_TEMP_VAR = "builder"; - private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String toBuilderMethodName, String builderClassName, JavacNode type, List typeParams, java.util.List builderFields, boolean fluent, JCAnnotation ast, AccessLevel access) { + private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String toBuilderMethodName, String builderClassName, JavacNode type, List typeParams, java.util.List builderFields, boolean fluent, JCAnnotation ast, AccessLevel access, String prefix) { // return new ThingieBuilder().setA(this.a).setB(this.b); JavacTreeMaker maker = type.getTreeMaker(); @@ -549,7 +549,13 @@ public class HandleBuilder extends JavacAnnotationHandler { JCExpression invoke = call; ListBuffer statements = new ListBuffer(); for (BuilderFieldData bfd : builderFields) { - Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString())); + String prefixedSetterName; + if(fluent) { + prefixedSetterName = prefix.isEmpty() ? bfd.name.toString() : HandlerUtil.buildAccessorName(prefix, bfd.name.toString()); + } else { + prefixedSetterName = HandlerUtil.buildAccessorName(prefix, bfd.name.toString()); + } + Name setterName = type.toName(prefixedSetterName); 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++) { -- cgit From b7e42d13ea98e280914f09f91fc03f355ea9682b Mon Sep 17 00:00:00 2001 From: Caleb Brinkman Date: Mon, 28 Oct 2019 13:48:59 -0500 Subject: Fix last test --- src/core/lombok/javac/handlers/HandleBuilder.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/core/lombok/javac/handlers/HandleBuilder.java') diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index a7235a40..46cb9b9a 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -826,13 +826,6 @@ public class HandleBuilder extends JavacAnnotationHandler { private void makePrefixedSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List 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) { @@ -841,6 +834,13 @@ public class HandleBuilder extends JavacAnnotationHandler { setterName = HandlerUtil.buildAccessorName(setterPrefix, paramName.toString()); } + for (JavacNode child : builderType.down()) { + if (child.getKind() != Kind.METHOD) continue; + JCMethodDecl methodDecl = (JCMethodDecl) child.get(); + Name existingName = methodDecl.name; + if (existingName.equals(builderType.toName(setterName)) && !isTolerate(fieldNode, methodDecl)) return; + } + JavacTreeMaker maker = fieldNode.getTreeMaker(); List methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); -- cgit From 870937a46f912592206abda57638d142ae81e763 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 11 Dec 2019 01:48:16 +0100 Subject: [builder] setterPrefix option code review, formatting cleanup, and docs --- doc/changelog.markdown | 1 + src/core/lombok/Builder.java | 11 ++-- .../eclipse/handlers/EclipseSingularsRecipes.java | 17 ++--- .../lombok/eclipse/handlers/HandleBuilder.java | 66 +++---------------- .../singulars/EclipseJavaUtilMapSingularizer.java | 20 +++--- src/core/lombok/javac/handlers/HandleBuilder.java | 73 +++++----------------- .../javac/handlers/JavacSingularsRecipes.java | 32 ++++------ website/templates/features/Builder.html | 6 +- 8 files changed, 64 insertions(+), 162 deletions(-) (limited to 'src/core/lombok/javac/handlers/HandleBuilder.java') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 44b7f553..b859c5a8 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -3,6 +3,7 @@ Lombok Changelog ### v1.18.11 "Edgy Guinea Pig" * PLATFORM: Support for JDK13 (including `yield` in switch expressions, as well as delombok having a nicer style for arrow-style switch blocks, and text blocks). +* FEATURE: You can now configure a builder's 'setter' prefixes via `@Builder(setterPrefix = "set")` for example. We discourage doing this, but if some library you use requires them, have at it. [Pull Request #2174](https://github.com/rzwitserloot/lombok/pull/2174], [Issue #1805](https://github.com/rzwitserloot/lombok/issues/1805). ### v1.18.10 (September 10th, 2019) diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index fcbe1a09..4ca3da65 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -155,14 +155,17 @@ public @interface Builder { 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. + * Prefix to prepend to 'set' methods in the generated builder class. By default, generated methods do not include a prefix. * - * For example, a method normally generated as {@code someField(String someField)} would instead be generated as {@code withSomeField(String someField)} + * For example, a method normally generated as {@code someField(String someField)} would instead be + * generated as {@code withSomeField(String someField)} if using {@code @Builder(setterPrefix = "with")}. * * Note that using "with" to prefix builder setter methods is strongly discouraged as as "with" normally * suggests immutable data structures, and builders by definition are mutable objects. - * + * + * For {@code @Singular} fields, the generated methods are called {@code withName}, {@code withNames}, and {@code clearNames}, instead of + * the default {@code name}, {@code names}, and {@code clearNames}. + * * @return The prefix to prepend to generated method names. */ String setterPrefix() default ""; diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index ce5a1b4c..cb2ebe18 100755 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -129,16 +129,9 @@ public class EclipseSingularsRecipes { private final ASTNode source; public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source) { - this.annotation = annotation; - this.singularName = singularName; - this.pluralName = pluralName; - this.typeArgs = typeArgs; - this.targetFqn = targetFqn; - this.singularizer = singularizer; - this.source = source; - this.setterPrefix = new char[0]; + this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, source, new char[0]); } - + public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source, char[] setterPrefix) { this.annotation = annotation; this.singularName = singularName; @@ -149,7 +142,7 @@ public class EclipseSingularsRecipes { this.source = source; this.setterPrefix = setterPrefix; } - + public void setGeneratedByRecursive(ASTNode target) { SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(source); @@ -177,11 +170,11 @@ public class EclipseSingularsRecipes { public char[] getPluralName() { return pluralName; } - + public char[] getSetterPrefix() { return setterPrefix; } - + public List getTypeArgs() { return typeArgs; } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 40098eb4..70978e23 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -1,16 +1,16 @@ /* * Copyright (C) 2013-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 * 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 @@ -567,13 +567,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { Expression receiver = invoke; List statements = null; for (BuilderFieldData bfd : builderFields) { - String setterPrefix = prefix.isEmpty() ? "set" : prefix; - String setterName; - if(fluent) { - setterName = prefix.isEmpty() ? new String(bfd.name) : HandlerUtil.buildAccessorName(setterPrefix, new String(bfd.name)); - } else { - setterName = HandlerUtil.buildAccessorName(setterPrefix, new String(bfd.name)); - } + String setterName = new String(bfd.name); + String setterPrefix = !prefix.isEmpty() ? prefix : fluent ? "" : "set"; + if (!setterPrefix.isEmpty()) setterName = HandlerUtil.buildAccessorName(setterPrefix, setterName); + MessageSend ms = new MessageSend(); Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2]; @@ -868,51 +865,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { private static final AbstractMethodDeclaration[] EMPTY = {}; - public void makeSetterMethodsForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access, EclipseNode originalFieldNode) { - boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); - if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(cfv, builderType, deprecate, bfd.createdFields.get(0), bfd.name, bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access, originalFieldNode); - } else { - bfd.singularData.getSingularizer().generateMethods(cfv, bfd.singularData, deprecate, builderType, fluent, chain, access); - } - } - - private void makeSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] paramName, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access, EclipseNode originalFieldNode) { - TypeDeclaration td = (TypeDeclaration) builderType.get(); - AbstractMethodDeclaration[] existing = td.methods; - ASTNode source = sourceNode.get(); - 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 setterName = fluent ? new String(paramName) : HandlerUtil.buildAccessorName("set", new String(paramName)); - - List 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.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) { @@ -988,9 +940,9 @@ public class HandleBuilder extends EclipseAnnotationHandler { /** * Returns the explicitly requested singular annotation on this node (field * or parameter), or null if there's no {@code @Singular} annotation on it. - * + * * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation. - * @param setterPrefix + * @param setterPrefix Explicitly requested setter prefix. */ private SingularData getSingularData(EclipseNode node, ASTNode source, final String setterPrefix) { for (EclipseNode child : node.down()) { diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index 4ceafd1e..b0223c50 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -28,7 +28,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.logging.Handler; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; @@ -63,7 +62,6 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker; import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker; import lombok.eclipse.handlers.HandleNonNull; -import org.objectweb.asm.Handle; @ProviderFor(EclipseSingularizer.class) public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer { @@ -247,12 +245,11 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer valueParam.annotations = typeUseAnnsValue; md.arguments = new Argument[] {keyParam, valueParam}; md.returnType = returnType; - + String name = new String(data.getSingularName()); - String setterPrefix = new String(data.getSetterPrefix()); - String prefixedSingularName = setterPrefix.isEmpty() ? name : HandlerUtil.buildAccessorName(setterPrefix, name); - String setterName = fluent ? prefixedSingularName : HandlerUtil.buildAccessorName("put", name); - + String setterPrefix = data.getSetterPrefix().length > 0 ? new String(data.getSetterPrefix()) : fluent ? "" : "put"; + String setterName = HandlerUtil.buildAccessorName(setterPrefix, name); + md.selector = setterName.toCharArray(); md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); @@ -317,12 +314,11 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer Argument param = new Argument(data.getPluralName(), 0, paramType, ClassFileConstants.AccFinal); md.arguments = new Argument[] {param}; md.returnType = returnType; - + String name = new String(data.getPluralName()); - String setterPrefix = new String(data.getSetterPrefix()); - String prefixedSingularName = setterPrefix.isEmpty() ? name : HandlerUtil.buildAccessorName(setterPrefix, name); - String setterName = fluent ? prefixedSingularName : HandlerUtil.buildAccessorName("put", name); - + String setterPrefix = data.getSetterPrefix().length > 0 ? new String(data.getSetterPrefix()) : fluent ? "" : "put"; + String setterName = HandlerUtil.buildAccessorName(setterPrefix, name); + md.selector = setterName.toCharArray(); md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 46cb9b9a..349b1382 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 { @Override public void handle(AnnotationValues 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; @@ -549,12 +549,10 @@ public class HandleBuilder extends JavacAnnotationHandler { JCExpression invoke = call; ListBuffer statements = new ListBuffer(); for (BuilderFieldData bfd : builderFields) { - String prefixedSetterName; - if(fluent) { - prefixedSetterName = prefix.isEmpty() ? bfd.name.toString() : HandlerUtil.buildAccessorName(prefix, bfd.name.toString()); - } else { - prefixedSetterName = HandlerUtil.buildAccessorName(prefix, bfd.name.toString()); - } + String setterPrefix = !prefix.isEmpty() ? prefix : fluent ? "" : "set"; + String prefixedSetterName = bfd.name.toString(); + if (!setterPrefix.isEmpty()) prefixedSetterName = HandlerUtil.buildAccessorName(setterPrefix, prefixedSetterName); + Name setterName = type.toName(prefixedSetterName); JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2]; if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { @@ -782,37 +780,6 @@ public class HandleBuilder extends JavacAnnotationHandler { for (JCVariableDecl gen : generated) recursiveSetGeneratedBy(gen, source, builderType.getContext()); } - public void makeSetterMethodsForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain, AccessLevel access) { - boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); - if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(cfv, builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.name, fieldNode.nameOfSetFlag, source, fluent, chain, fieldNode.annotations, fieldNode.originalFieldNode, access); - } else { - fieldNode.singularData.getSingularizer().generateMethods(cfv, fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain, access); - } - } - - private void makeSimpleSetterMethodForBuilder(CheckerFrameworkVersion cfv, JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name paramName, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, List 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 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) { @@ -822,27 +789,21 @@ public class HandleBuilder extends JavacAnnotationHandler { 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 annosOnParam, JavacNode originalFieldNode, AccessLevel access, String prefix) { - Name fieldName = ((JCVariableDecl) fieldNode.get()).name; - - 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()); - } - + String setterPrefix = !prefix.isEmpty() ? prefix : fluent ? "" : "set"; + String setterName = HandlerUtil.buildAccessorName(setterPrefix, paramName.toString()); + Name setterName_ = builderType.toName(setterName); + for (JavacNode child : builderType.down()) { if (child.getKind() != Kind.METHOD) continue; JCMethodDecl methodDecl = (JCMethodDecl) child.get(); Name existingName = methodDecl.name; - if (existingName.equals(builderType.toName(setterName)) && !isTolerate(fieldNode, methodDecl)) return; + if (existingName.equals(setterName_) && !isTolerate(fieldNode, methodDecl)) return; } - + JavacTreeMaker maker = fieldNode.getTreeMaker(); - + List methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, chain, source, methodAnns, annosOnParam); if (cfv.generateCalledMethods()) { @@ -853,10 +814,10 @@ public class HandleBuilder extends JavacAnnotationHandler { } 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 typeParams, JCAnnotation ast, AccessLevel access) { JavacTreeMaker maker = tdParent.getTreeMaker(); int modifiers = toJavacModifier(access); @@ -882,9 +843,9 @@ public class HandleBuilder extends JavacAnnotationHandler { * or parameter), or null if there's no {@code @Singular} annotation on it. * * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation. - * @param setterPrefix + * @param setterPrefix Explicitly requested setter prefix. */ - private SingularData getSingularData(JavacNode node, final String setterPrefix) { + private SingularData getSingularData(JavacNode node, String setterPrefix) { for (JavacNode child : node.down()) { if (!annotationTypeMatches(Singular.class, child)) continue; Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name; diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 87081dde..86f148ca 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -122,15 +122,9 @@ public class JavacSingularsRecipes { private final String setterPrefix; public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer) { - this.annotation = annotation; - this.singularName = singularName; - this.pluralName = pluralName; - this.typeArgs = typeArgs; - this.targetFqn = targetFqn; - this.singularizer = singularizer; - this.setterPrefix = ""; + this(annotation, singularName, pluralName, typeArgs, targetFqn, singularizer, ""); } - + public SingularData(JavacNode annotation, Name singularName, Name pluralName, List typeArgs, String targetFqn, JavacSingularizer singularizer, String setterPrefix) { this.annotation = annotation; this.singularName = singularName; @@ -152,8 +146,10 @@ public class JavacSingularsRecipes { public Name getPluralName() { return pluralName; } - - public String getSetterPrefix() { return setterPrefix; } + + public String getSetterPrefix() { + return setterPrefix; + } public List getTypeArgs() { return typeArgs; @@ -296,12 +292,9 @@ public class JavacSingularsRecipes { List params = generateSingularMethodParameters(maker, data, builderType, source); Name name = data.getSingularName(); String setterPrefix = data.getSetterPrefix(); - Name prefixedSingularName = setterPrefix.isEmpty() ? name : - builderType.toName(HandlerUtil.buildAccessorName(setterPrefix, name.toString())); - - name = fluent ? prefixedSingularName : builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName(), - name.toString())); - + if (setterPrefix.isEmpty() && !fluent) setterPrefix = getAddMethodName(); + if (!setterPrefix.isEmpty()) name = builderType.toName(HandlerUtil.buildAccessorName(setterPrefix, name.toString())); + statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access); } @@ -328,10 +321,9 @@ public class JavacSingularsRecipes { private void generatePluralMethod(CheckerFrameworkVersion cfv, boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent, AccessLevel access) { ListBuffer statements = generatePluralMethodStatements(maker, data, builderType, source); Name name = data.getPluralName(); - - Name prefixedSingularName = data.getSetterPrefix().isEmpty() ? name : builderType.toName(HandlerUtil.buildAccessorName(data.getSetterPrefix(), data.getPluralName().toString())); - name = fluent ? prefixedSingularName - : builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName() + "All", name.toString())); + String setterPrefix = data.getSetterPrefix(); + if (setterPrefix.isEmpty() && !fluent) setterPrefix = getAddMethodName() + "All"; + if (!setterPrefix.isEmpty()) name = builderType.toName(HandlerUtil.buildAccessorName(setterPrefix, name.toString())); JCExpression paramType = getPluralMethodParamType(builderType); paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs(), source); long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); diff --git a/website/templates/features/Builder.html b/website/templates/features/Builder.html index d22877ea..08ff1ec8 100644 --- a/website/templates/features/Builder.html +++ b/website/templates/features/Builder.html @@ -68,10 +68,12 @@ If you want toBuilder() (default: no)
  • The access level of all generated elements (default: public). +
  • + (discouraged) If you want your builder's 'set' methods to have a prefix, i.e. Person.builder().setName("Jane").build() instead of Person.builder().name("Jane").build() and what it should be.
  • Example usage where all options are changed from their defaults:
    - @Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true, access = AccessLevel.PRIVATE)
    + @Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true, access = AccessLevel.PRIVATE, setterPrefix = "set")

    @@ -132,6 +134,8 @@ If lombok cannot singularize your identifier, or it is ambiguous, lombok will generate an error and force you to explicitly specify the singular name.

    The snippet below does not show what lombok generates for a @Singular field/parameter because it is rather complicated. You can view a snippet here. +

    + If also using setterPrefix = "with", the generated names are, for example, withName (add 1 name), withNames (add many names), and clearNames (reset all names).

    -- cgit