diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleBuilder.java | 116 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleConstructor.java | 72 |
2 files changed, 124 insertions, 64 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 58e08b0d..ebeb82ea 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -25,6 +25,7 @@ import static lombok.eclipse.Eclipse.*; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -39,6 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; @@ -49,6 +51,7 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; @@ -80,6 +83,7 @@ import lombok.core.HandlerPriority; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists; @@ -99,6 +103,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } private static class BuilderFieldData { + EclipseNode fieldNode; TypeReference type; char[] rawName; char[] name; @@ -188,6 +193,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; BuilderFieldData bfd = new BuilderFieldData(); + bfd.fieldNode = fieldNode; bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.type; @@ -205,8 +211,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } boolean callBuilderBasedSuperConstructor = inherit && td.superclass != null; - new HandleConstructor().generateConstructor(tdParent, extendable ? AccessLevel.PROTECTED : AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, - Collections.<Annotation>emptyList(), annotationNode, extendable ? builderClassName : null, callBuilderBasedSuperConstructor); + if (extendable) { + generateConstructor(tdParent, builderFields, annotationNode, + builderClassName, callBuilderBasedSuperConstructor); + } else { + new 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; @@ -427,7 +438,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, - annotationNode, Collections.<Annotation>emptyList(), null, false); + annotationNode, Collections.<Annotation>emptyList()); if (cd != null) injectMethod(builderType, cd); } @@ -524,6 +535,105 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } + /** + * @param builderClassnameAsParameter + * if {@code != null}, the only parameter of the constructor will + * be a builder with this classname; the constructor will then + * use the values within this builder to assign the fields of new + * instances. + * @param callBuilderBasedSuperConstructor + * if {@code true}, the constructor will explicitly call a super + * constructor with the builder as argument. Requires + * {@code builderClassAsParameter != null}. + */ + private void generateConstructor(EclipseNode typeNode, List<BuilderFieldData> builderFields, EclipseNode sourceNode, + String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + + if (builderClassnameAsParameter == null || builderClassnameAsParameter.isEmpty()) { + typeNode.addError("A builder-based constructor requires a non-empty 'builderClassnameAsParameter' value."); + } + + AccessLevel level = AccessLevel.PROTECTED; + ASTNode source = sourceNode.get(); + + TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); + long p = (long) source.sourceStart << 32 | source.sourceEnd; + + boolean isEnum = (((TypeDeclaration) typeNode.get()).modifiers & ClassFileConstants.AccEnum) != 0; + if (isEnum) { + level = AccessLevel.PRIVATE; + } + +// boolean suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); + + constructor.modifiers = toEclipseModifier(level); + constructor.selector = typeDeclaration.name; + if (callBuilderBasedSuperConstructor) { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); + constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; + } else { + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); + } + constructor.constructorCall.sourceStart = source.sourceStart; + constructor.constructorCall.sourceEnd = source.sourceEnd; + constructor.thrownExceptions = null; + constructor.typeParameters = null; + constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; + constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; + constructor.arguments = null; + + List<Statement> assigns = new ArrayList<Statement>(); + List<Statement> nullChecks = new ArrayList<Statement>(); + + for (BuilderFieldData fieldNode : builderFields) { + char[] fieldName = removePrefixFromField(fieldNode.fieldNode); + FieldReference thisX = new FieldReference(fieldNode.rawName, p); + int s = (int) (p >> 32); + int e = (int) p; + thisX.receiver = new ThisReference(s, e); + + if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { + fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, assigns, fieldNode.name); + } + + Expression assignmentExpr; + if (builderClassnameAsParameter != null) { + char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; + long[] positions = new long[] {p, p}; + assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); + } else { + assignmentExpr = new SingleNameReference(fieldName, p); + } + + Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); + assigns.add(assignment); + Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.fieldNode.get(), NON_NULL_PATTERN); + if (nonNulls.length != 0) { + Statement nullCheck = generateNullCheck((FieldDeclaration)fieldNode.fieldNode.get(), sourceNode); + if (nullCheck != null) { + nullChecks.add(nullCheck); + } + } + } + + nullChecks.addAll(assigns); + constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); + constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; + + // Generate annotations that must be put on the generated method, and attach them. +// Annotation[] constructorProperties = null; +// if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !HandleConstructor.isLocalType(typeNode)) { +// constructorProperties = HandleConstructor.createConstructorProperties(source, builderFields); +// } + + constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); + + injectMethod(typeNode, constructor); + } + private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) { List<Statement> statements = new ArrayList<Statement>(); diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index a8b823b2..49b09231 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -64,12 +64,10 @@ import org.eclipse.jdt.internal.compiler.ast.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.LongLiteral; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; -import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; -import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; @@ -97,7 +95,7 @@ public class HandleConstructor { List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList(); List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode); - new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode, null, false); + new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); } } @@ -120,7 +118,7 @@ public class HandleConstructor { new HandleConstructor().generateConstructor( typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, - onConstructor, annotationNode, null, false); + onConstructor, annotationNode); } } @@ -179,7 +177,7 @@ public class HandleConstructor { new HandleConstructor().generateConstructor( typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, - onConstructor, annotationNode, null, false); + onConstructor, annotationNode); } } @@ -201,34 +199,23 @@ public class HandleConstructor { EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode, null, false); + generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode); } public void generateAllArgsConstructor( EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode, null, false); + generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode); } public enum SkipIfConstructorExists { YES, NO, I_AM_BUILDER; } - /** - * @param builderClassnameAsParameter - * if {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassAsParameter != null}. - */ public void generateConstructor( EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, - List<Annotation> onConstructor, EclipseNode sourceNode, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { + List<Annotation> onConstructor, EclipseNode sourceNode) { ASTNode source = sourceNode.get(); boolean staticConstrRequired = staticName != null && !staticName.equals(""); @@ -263,7 +250,7 @@ public class HandleConstructor { ConstructorDeclaration constr = createConstructor( staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault, - sourceNode, onConstructor, builderClassnameAsParameter, callBuilderBasedSuperConstructor); + sourceNode, onConstructor); injectMethod(typeNode, constr); if (staticConstrRequired) { MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.<EclipseNode>emptyList() : fields, source); @@ -303,28 +290,9 @@ public class HandleConstructor { return new Annotation[] { ann }; } - /** - * @param builderClassnameAsParameter - * if {@code != null}, the only parameter of the constructor will - * be a builder with this classname; the constructor will then - * use the values within this builder to assign the fields of new - * instances. - * @param callBuilderBasedSuperConstructor - * if {@code true}, the constructor will explicitly call a super - * constructor with the builder as argument. Requires - * {@code builderClassnameAsParameter != null}. - */ public static ConstructorDeclaration createConstructor( AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault, - EclipseNode sourceNode, List<Annotation> onConstructor, - String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { - - if (builderClassnameAsParameter != null && builderClassnameAsParameter.isEmpty()) { - builderClassnameAsParameter = null; - } - if (callBuilderBasedSuperConstructor && builderClassnameAsParameter == null) { - type.addError("Calling a builder-based superclass constructor ('callBuilderBasedSuperConstructor') requires a non-empty 'builderClassnameAsParameter' value."); - } + EclipseNode sourceNode, List<Annotation> onConstructor) { ASTNode source = sourceNode.get(); TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get()); @@ -345,12 +313,7 @@ public class HandleConstructor { constructor.modifiers = toEclipseModifier(level); constructor.selector = typeDeclaration.name; - if (callBuilderBasedSuperConstructor) { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super); - constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)}; - } else { - constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); - } + constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper); constructor.constructorCall.sourceStart = source.sourceStart; constructor.constructorCall.sourceEnd = source.sourceEnd; constructor.thrownExceptions = null; @@ -373,16 +336,7 @@ public class HandleConstructor { int e = (int) p; thisX.receiver = new ThisReference(s, e); - Expression assignmentExpr; - if (allToDefault) { - assignmentExpr = getDefaultExpr(field.type, s, e); - } else if (builderClassnameAsParameter != null) { - char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName}; - long[] positions = new long[] {p, p}; - assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e); - } else { - assignmentExpr = new SingleNameReference(fieldName, p); - } + Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p); Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32); @@ -403,11 +357,7 @@ public class HandleConstructor { nullChecks.addAll(assigns); constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); - if (builderClassnameAsParameter != null) { - constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; - } else { - constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); - } + constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); /* Generate annotations that must be put on the generated method, and attach them. */ { Annotation[] constructorProperties = null; |