diff options
Diffstat (limited to 'src')
8 files changed, 323 insertions, 149 deletions
diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index dfa5ecb5..fcbe1a09 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -153,6 +153,19 @@ 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)} + * + * 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. + * + * @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/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 483431cc..ce5a1b4c 100755 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -122,6 +122,7 @@ public class EclipseSingularsRecipes { private final EclipseNode annotation; private final char[] singularName; private final char[] pluralName; + private final char[] setterPrefix; private final List<TypeReference> typeArgs; private final String targetFqn; private final EclipseSingularizer singularizer; @@ -135,8 +136,20 @@ public class EclipseSingularsRecipes { this.targetFqn = targetFqn; this.singularizer = singularizer; this.source = source; + this.setterPrefix = new char[0]; } - + + public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List<TypeReference> typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source, char[] setterPrefix) { + this.annotation = annotation; + this.singularName = singularName; + this.pluralName = pluralName; + this.typeArgs = typeArgs; + this.targetFqn = targetFqn; + this.singularizer = singularizer; + this.source = source; + this.setterPrefix = setterPrefix; + } + public void setGeneratedByRecursive(ASTNode target) { SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(source); @@ -164,7 +177,11 @@ public class EclipseSingularsRecipes { public char[] getPluralName() { return pluralName; } - + + public char[] getSetterPrefix() { + return setterPrefix; + } + public List<TypeReference> getTypeArgs() { return typeArgs; } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 8bfdeb65..aab97e18 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 @@ -101,17 +101,17 @@ import lombok.experimental.NonFinal; @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleBuilder extends EclipseAnnotationHandler<Builder> { private HandleConstructor handleConstructor = new HandleConstructor(); - + private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray(); private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray(); - + private static final boolean toBoolean(Object expr, boolean defaultValue) { if (expr == null) return defaultValue; if (expr instanceof FalseLiteral) return false; if (expr instanceof TrueLiteral) return true; return ((Boolean) expr).booleanValue(); } - + static class BuilderFieldData { Annotation[] annotations; TypeReference type; @@ -124,10 +124,10 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { ObtainVia obtainVia; EclipseNode obtainViaNode; EclipseNode originalFieldNode; - + List<EclipseNode> createdFields = new ArrayList<EclipseNode>(); } - + private static boolean equals(String a, char[] b) { if (a.length() != b.length) return false; for (int i = 0; i < b.length; i++) { @@ -135,7 +135,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } return true; } - + private static boolean equals(String a, char[][] b) { if (a == null || a.isEmpty()) return b.length == 0; String[] aParts = a.split("\\."); @@ -145,24 +145,24 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } return true; } - + private static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'}; private static final char[] SET_PREFIX = {'$', 's', 'e', 't'}; private static final char[] VALUE_PREFIX = {'$', 'v', 'a', 'l', 'u', 'e'}; - + private static final char[] prefixWith(char[] prefix, char[] name) { char[] out = new char[prefix.length + name.length]; System.arraycopy(prefix, 0, out, 0, prefix.length); System.arraycopy(name, 0, out, prefix.length, name.length); return out; } - + @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(); AccessLevel accessForOuters = builderInstance.access(); if (accessForOuters == null) accessForOuters = AccessLevel.PUBLIC; @@ -171,22 +171,22 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { 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); boolean chain = toBoolean(annotation.getActualExpression("chain"), true); - + String builderMethodName = builderInstance.builderMethodName(); String buildMethodName = builderInstance.buildMethodName(); String builderClassName = builderInstance.builderClassName(); String toBuilderMethodName = "toBuilder"; boolean toBuilder = builderInstance.toBuilder(); List<char[]> typeArgsForToBuilder = null; - + if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; if (builderClassName == null) builderClassName = ""; - + boolean generateBuilderMethod; if (builderMethodName.isEmpty()) { generateBuilderMethod = false; @@ -195,74 +195,74 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } else { generateBuilderMethod = true; } - + if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; if (!builderClassName.isEmpty()) { if (!checkName("builderClassName", builderClassName, annotationNode)) return; } - + EclipseNode parent = annotationNode.up(); - + List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); TypeReference returnType; TypeParameter[] typeParams; TypeReference[] thrownExceptions; char[] nameOfStaticBuilderMethod; EclipseNode tdParent; - + EclipseNode fillParametersFrom = parent.get() instanceof AbstractMethodDeclaration ? parent : null; boolean addCleaning = false; boolean isStatic = true; - + List<EclipseNode> nonFinalNonDefaultedFields = null; - + if (builderClassName.isEmpty()) builderClassName = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME); if (builderClassName == null || builderClassName.isEmpty()) builderClassName = "*Builder"; boolean replaceNameInBuilderClassName = builderClassName.contains("*"); - + if (parent.get() instanceof TypeDeclaration) { tdParent = parent; TypeDeclaration td = (TypeDeclaration) tdParent.get(); - + List<EclipseNode> allFields = new ArrayList<EclipseNode>(); boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, 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)); - + Annotation[] copyableAnnotations = findCopyableAnnotations(fieldNode); - + BuilderFieldData bfd = new BuilderFieldData(); bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.builderFieldName = bfd.name; bfd.annotations = copyAnnotations(fd, copyableAnnotations); bfd.type = fd.type; - bfd.singularData = getSingularData(fieldNode, ast); + bfd.singularData = getSingularData(fieldNode, ast, builderInstance.setterPrefix()); bfd.originalFieldNode = fieldNode; - + if (bfd.singularData != null && isDefault != null) { isDefault.addError("@Builder.Default and @Singular cannot be mixed."); isDefault = null; } - + if (fd.initialization == null && isDefault != null) { isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); isDefault = null; } - + if (fd.initialization != null && isDefault == null) { if (isFinal) continue; if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<EclipseNode>(); nonFinalNonDefaultedFields.add(fieldNode); } - + if (isDefault != null) { bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name); bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX); bfd.builderFieldName = prefixWith(bfd.name, VALUE_PREFIX); - + MethodDeclaration md = generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast); if (md != null) injectMethod(tdParent, md); } @@ -270,10 +270,10 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { builderFields.add(bfd); allFields.add(fieldNode); } - + handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, Collections.<Annotation>emptyList(), annotationNode); - + returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; thrownExceptions = null; @@ -286,7 +286,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { annotationNode.addError("@Builder is not supported on constructors with constructor type parameters."); return; } - + tdParent = parent.up(); TypeDeclaration td = (TypeDeclaration) tdParent.get(); returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); @@ -299,7 +299,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { MethodDeclaration md = (MethodDeclaration) parent.get(); tdParent = parent.up(); isStatic = md.isStatic(); - + if (toBuilder) { final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type."; char[] token; @@ -308,7 +308,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { annotationNode.addError(TO_BUILDER_NOT_SUPPORTED); return; } - + if (md.returnType instanceof SingleTypeReference) { token = ((SingleTypeReference) md.returnType).token; } else if (md.returnType instanceof QualifiedTypeReference) { @@ -321,17 +321,17 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { annotationNode.addError(TO_BUILDER_NOT_SUPPORTED); return; } - + if (pkg != null && !equals(parent.getPackageDeclaration(), pkg)) { annotationNode.addError(TO_BUILDER_NOT_SUPPORTED); return; } - + if (tdParent == null || !equals(tdParent.getName(), token)) { annotationNode.addError(TO_BUILDER_NOT_SUPPORTED); return; } - + TypeParameter[] tpOnType = ((TypeDeclaration) tdParent.get()).typeParameters; TypeParameter[] tpOnMethod = md.typeParameters; TypeReference[][] tpOnRet_ = null; @@ -341,7 +341,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } else if (md.returnType instanceof ParameterizedQualifiedTypeReference) { tpOnRet_ = ((ParameterizedQualifiedTypeReference) md.returnType).typeArguments; } - + if (tpOnRet_ != null) for (int i = 0; i < tpOnRet_.length - 1; i++) { if (tpOnRet_[i] != null && tpOnRet_[i].length > 0) { annotationNode.addError("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate."); @@ -350,11 +350,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } TypeReference[] tpOnRet = tpOnRet_ == null ? null : tpOnRet_[tpOnRet_.length - 1]; typeArgsForToBuilder = new ArrayList<char[]>(); - + // Every typearg on this method needs to be found in the return type, but the reverse is not true. // We also need to 'map' them. - - + + if (tpOnMethod != null) for (TypeParameter onMethod : tpOnMethod) { int pos = -1; if (tpOnRet != null) for (int i = 0; i < tpOnRet.length; i++) { @@ -366,11 +366,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { annotationNode.addError("@Builder(toBuilder=true) requires that each type parameter on the static method is part of the typeargs of the return value. Type parameter " + new String(onMethod.name) + " is not part of the return type."); return; } - + typeArgsForToBuilder.add(tpOnType[pos].name); } } - + returnType = copyType(md.returnType, ast); typeParams = md.typeParameters; thrownExceptions = md.thrownExceptions; @@ -394,41 +394,41 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { annotationNode.addError("Unexpected kind of return type on annotated method. Specify 'builderClassName' to solve this problem."); return; } - + if (Character.isLowerCase(token[0])) { char[] newToken = new char[token.length]; System.arraycopy(token, 1, newToken, 1, token.length - 1); newToken[0] = Character.toTitleCase(token[0]); token = newToken; } - + builderClassName = builderClassName.replace("*", new String(token)); } } else { annotationNode.addError("@Builder is only supported on types, constructors, and methods."); return; } - + if (fillParametersFrom != null) { for (EclipseNode param : fillParametersFrom.down()) { if (param.getKind() != Kind.ARGUMENT) continue; BuilderFieldData bfd = new BuilderFieldData(); Argument arg = (Argument) param.get(); - + Annotation[] copyableAnnotations = findCopyableAnnotations(param); - + bfd.rawName = arg.name; bfd.name = arg.name; bfd.builderFieldName = bfd.name; bfd.annotations = copyAnnotations(arg, copyableAnnotations); bfd.type = arg.type; - bfd.singularData = getSingularData(param, ast); + bfd.singularData = getSingularData(param, ast, builderInstance.setterPrefix()); bfd.originalFieldNode = param; addObtainVia(bfd, param); builderFields.add(bfd); } } - + EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, accessForOuters); @@ -454,7 +454,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } } - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { if (bfd.singularData.getSingularizer().requiresCleaning()) { @@ -473,7 +473,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } } - + generateBuilderFields(builderType, builderFields, ast); if (addCleaning) { FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1); @@ -483,18 +483,18 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { cleanDecl.traverse(new SetGeneratedByVisitor(ast), (MethodScope) null); injectFieldAndMarkGenerated(builderType, cleanDecl); } - + if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, annotationNode, Collections.<Annotation>emptyList()); if (cd != null) injectMethod(builderType, cd); } - + 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()); } - + { MemberExistsResult methodExists = methodExists(buildMethodName, builderType, -1); if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(buildMethodName, builderType, 0); @@ -503,7 +503,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (md != null) injectMethod(builderType, md); } } - + if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>(); for (BuilderFieldData bfd : builderFields) { @@ -514,18 +514,18 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD); if (md != null) injectMethod(builderType, md); } - + if (addCleaning) { MethodDeclaration cleanMethod = generateCleanMethod(builderFields, builderType, ast); if (cleanMethod != null) injectMethod(builderType, cleanMethod); } - + if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { MethodDeclaration md = generateBuilderMethod(cfv, isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast, accessForOuters); if (md != null) injectMethod(tdParent, md); } - + if (toBuilder) switch (methodExists(toBuilderMethodName, tdParent, 0)) { case EXISTS_BY_USER: annotationNode.addWarning("Not generating toBuilder() as it already exists."); @@ -539,23 +539,23 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { tps[i].name = typeArgsForToBuilder.get(i); } } - MethodDeclaration md = generateToBuilderMethod(cfv, toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters); - + MethodDeclaration md = generateToBuilderMethod(cfv, toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast, accessForOuters, builderInstance.setterPrefix()); + if (md != null) injectMethod(tdParent, md); } - + if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { for (EclipseNode fieldNode : nonFinalNonDefaultedFields) { fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); } } } - + private static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'}; - private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source, AccessLevel access) { + private MethodDeclaration generateToBuilderMethod(CheckerFrameworkVersion cfv, String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source, AccessLevel access, String prefix) { 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 = toEclipseModifier(access); @@ -563,14 +563,20 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); AllocationExpression invoke = new AllocationExpression(); invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p); - + Expression receiver = invoke; List<Statement> statements = null; for (BuilderFieldData bfd : builderFields) { - char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray(); + 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)); + } MessageSend ms = new MessageSend(); Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2]; - + if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray(); for (int i = 0; i < tgt.length; i++) { @@ -599,8 +605,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { tgt[i] = obtainExpr; } } - - ms.selector = setterName; + + ms.selector = setterName.toCharArray(); if (bfd.singularData == null) { ms.arguments = tgt; ms.receiver = receiver; @@ -613,7 +619,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { statements.add(new IfStatement(isNotNull, ms, pS, pE)); } } - + if (statements != null) { out.statements = new Statement[statements.size() + 2]; for (int i = 0; i < statements.size(); i++) out.statements[i + 1] = statements.get(i); @@ -627,25 +633,25 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } else { out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)}; } - + if (cfv.generateUnique()) { out.annotations = new Annotation[] {generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__UNIQUE)}; } - + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; } - + private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) { List<Statement> statements = new ArrayList<Statement>(); - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements); } } - + FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); thisUnclean.receiver = new ThisReference(0, 0); statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0)); @@ -658,15 +664,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return decl; } - + 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,12 +692,12 @@ 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; List<Statement> statements = new ArrayList<Statement>(); - + if (addCleaning) { FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); thisUnclean.receiver = new ThisReference(0, 0); @@ -700,13 +706,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { invokeClean.selector = CLEAN_METHOD_NAME; statements.add(new IfStatement(notClean, invokeClean, 0, 0)); } - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.builderFieldName, "this"); } } - + List<Expression> args = new ArrayList<Expression>(); for (BuilderFieldData bfd : builderFields) { if (bfd.nameOfSetFlag != null) { @@ -716,7 +722,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L); inv.selector = bfd.nameOfDefaultProvider; inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); - + args.add(new ConditionalExpression( new SingleNameReference(bfd.nameOfSetFlag, 0L), new SingleNameReference(bfd.builderFieldName, 0L), @@ -725,19 +731,19 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { args.add(new SingleNameReference(bfd.builderFieldName, 0L)); } } - + if (addCleaning) { FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); thisUnclean.receiver = new ThisReference(0, 0); statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0)); } - + out.modifiers = toEclipseModifier(access); out.selector = name.toCharArray(); out.thrownExceptions = copyTypes(thrownExceptions); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; out.returnType = returnType; - + if (staticName == null) { AllocationExpression allocationStatement = new AllocationExpression(); allocationStatement.type = copyType(out.returnType); @@ -750,7 +756,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0); else invoke.receiver = new QualifiedThisReference(new SingleTypeReference(type.up().getName().toCharArray(), 0) , 0, 0); - + invoke.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); invoke.arguments = args.isEmpty() ? null : args.toArray(new Expression[0]); if (returnType instanceof SingleTypeReference && Arrays.equals(TypeConstants.VOID, ((SingleTypeReference) returnType).token)) { @@ -767,20 +773,20 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return out; } - + private TypeReference[] typeParameterNames(TypeParameter[] typeParameters) { if (typeParameters == null) return null; - + TypeReference[] trs = new TypeReference[typeParameters.length]; for (int i = 0; i < trs.length; i++) { trs[i] = new SingleTypeReference(typeParameters[i].name, 0); } return trs; } - + public static MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; - + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); out.typeParameters = copyTypeParams(typeParameters, source); out.selector = methodName; @@ -790,15 +796,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { out.returnType = copyType(fd.type, source); out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)}; fd.initialization = null; - + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope); return out; } - + public MethodDeclaration generateBuilderMethod(CheckerFrameworkVersion cfv, 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 = toEclipseModifier(access); @@ -821,13 +827,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope); return out; } - + public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields, ASTNode source) { List<EclipseNode> existing = new ArrayList<EclipseNode>(); for (EclipseNode child : builderType.down()) { if (child.getKind() == Kind.FIELD) existing.add(child); } - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType)); @@ -838,7 +844,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (Arrays.equals(n, bfd.builderFieldName)) field = exists; if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists; } - + if (field == null) { FieldDeclaration fd = new FieldDeclaration(bfd.builderFieldName, 0, 0); fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -859,9 +865,9 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } } - + 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) { @@ -870,7 +876,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { 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; @@ -879,16 +885,67 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { 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<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; + + 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)); + } + + for (int i = 0; i < len; i++) { + if (!(existing[i] instanceof MethodDeclaration)) continue; + char[] existingName = existing[i].selector; + if (Arrays.equals(setterName.toCharArray(), existingName) && !isTolerate(fieldNode, existing[i])) return; + } + + 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 +954,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); @@ -916,7 +974,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } - + private void addObtainVia(BuilderFieldData bfd, EclipseNode node) { for (EclipseNode child : node.down()) { if (!annotationTypeMatches(ObtainVia.class, child)) continue; @@ -926,14 +984,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return; } } - + /** * 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 */ - private SingularData getSingularData(EclipseNode node, ASTNode source) { + private SingularData getSingularData(EclipseNode node, ASTNode source, final String setterPrefix) { for (EclipseNode child : node.down()) { if (!annotationTypeMatches(Singular.class, child)) continue; char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name; @@ -952,7 +1011,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } char[] singularName = explicitSingular.toCharArray(); - + TypeReference type = ((AbstractVariableDeclaration) node.get()).type; TypeReference[] typeArgs = null; String typeName; @@ -972,7 +1031,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } else { typeName = type.toString(); } - + String targetFqn = EclipseSingularsRecipes.get().toQualified(typeName); EclipseSingularizer singularizer = EclipseSingularsRecipes.get().getSingularizer(targetFqn); if (singularizer == null) { @@ -980,9 +1039,9 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return null; } - return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source); + return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source, setterPrefix.toCharArray()); } - + return null; } } 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/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index 53ea15a6..e3a99008 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -150,7 +150,8 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula param.annotations = typeUseAnns; md.arguments = new Argument[] {param}; md.returnType = returnType; - md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("add", 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); @@ -181,7 +182,8 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula 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("addAll", 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/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index 8684987f..4ceafd1e 100755 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -28,6 +28,7 @@ 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; @@ -62,6 +63,7 @@ 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 { @@ -245,7 +247,13 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer valueParam.annotations = typeUseAnnsValue; md.arguments = new Argument[] {keyParam, valueParam}; md.returnType = returnType; - md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("put", new String(data.getSingularName())).toCharArray(); + + 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); + + md.selector = setterName.toCharArray(); md.annotations = generateSelfReturnAnnotations(deprecate, cfv, data.getSource()); data.setGeneratedByRecursive(md); @@ -309,7 +317,13 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer 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("putAll", new String(data.getPluralName())).toCharArray(); + + 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); + + md.selector = setterName.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 de2bdde1..46cb9b9a 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; @@ -180,7 +180,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { 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<Builder> { 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); @@ -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()); } { @@ -487,7 +487,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } 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<Builder> { } private static final String BUILDER_TEMP_VAR = "builder"; - private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String toBuilderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, boolean fluent, JCAnnotation ast, AccessLevel access) { + private JCMethodDecl generateToBuilderMethod(CheckerFrameworkVersion cfv, String toBuilderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, boolean fluent, JCAnnotation ast, AccessLevel access, String prefix) { // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b); JavacTreeMaker maker = type.getTreeMaker(); @@ -549,7 +549,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCExpression invoke = call; ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); for (BuilderFieldData bfd : builderFields) { - Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString())); + 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++) { @@ -622,12 +628,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 +648,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 +793,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; + + 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()); + } + + 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<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 +853,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); @@ -838,8 +882,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { * 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; @@ -881,7 +926,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { 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 fa384df3..87081dde 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -119,6 +119,7 @@ public class JavacSingularsRecipes { private final List<JCExpression> typeArgs; private final String targetFqn; private final JavacSingularizer singularizer; + private final String setterPrefix; public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer) { this.annotation = annotation; @@ -127,6 +128,17 @@ public class JavacSingularsRecipes { this.typeArgs = typeArgs; this.targetFqn = targetFqn; this.singularizer = singularizer; + this.setterPrefix = ""; + } + + public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> 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() { @@ -140,6 +152,8 @@ public class JavacSingularsRecipes { public Name getPluralName() { return pluralName; } + + public String getSetterPrefix() { return setterPrefix; } public List<JCExpression> getTypeArgs() { return typeArgs; @@ -281,8 +295,13 @@ public class JavacSingularsRecipes { ListBuffer<JCStatement> statements = generateSingularMethodStatements(maker, data, builderType, source); List<JCVariableDecl> params = generateSingularMethodParameters(maker, data, builderType, source); Name name = data.getSingularName(); - if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName(), name.toString())); - + 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())); + statements.prepend(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, params, access); } @@ -309,7 +328,10 @@ 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<JCStatement> statements = generatePluralMethodStatements(maker, data, builderType, source); Name name = data.getPluralName(); - if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName() + "All", name.toString())); + + 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())); JCExpression paramType = getPluralMethodParamType(builderType); paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs(), source); long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); |