diff options
-rw-r--r-- | doc/changelog.markdown | 1 | ||||
-rw-r--r-- | src/core/lombok/NoArgsConstructor.java | 11 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleConstructor.java | 77 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleData.java | 2 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleConstructor.java | 50 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleData.java | 2 |
6 files changed, 104 insertions, 39 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 01ddfb45..2acfd7fd 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -13,6 +13,7 @@ Lombok Changelog * ENHANCEMENT: All field accesses generated by lombok are now qualified (like so: `this.fieldName`). For those who have a warning configured for unqualified field access, those should no longer occur. [Issue #48](http://code.google.com/p/projectlombok/issues/detail?id=48) * ENHANCEMENT: All fields and methods generated by lombok now get `@SuppressWarnings("all")` attached to avoid such warnings as missing javadoc, for those of you who have that warning enabled. [Issue #47](http://code.google.com/p/projectlombok/issues/detail?id=47) * FEATURE: Three new annotations, `@NoArgsConstructor`, `@RequiredArgsConstructor` and `@AllArgsConstructor` have been added. These split off `@Data`'s ability to generate constructors, and also allow you to finetune what kind of constructor you want. In addition, by using these annotations, you can force generation of constructors even if you have your own. [Issue #79](http://code.google.com/p/projectlombok/issues/detail?id=79) +* FEATURE: Constructors generated by lombok now include a `@java.beans.ConstructorProperties` annotation. This does mean these constructors no longer work in java 1.5, as this is a java 1.6 feature. The annotation can be suppressed by setting `suppressConstructorProperties` to `true` in a `@RequiredArgsConstructor` or `@AllArgsConstructor` annotation. [Issue #122](http://code.google.com/p/projectlombok/issues/detail?id=122) ### v0.9.2 "Hailbunny" (December 15th, 2009) * preliminary support for lombok on NetBeans! - thanks go to Jan Lahoda from NetBeans. [Issue #20](http://code.google.com/p/projectlombok/issues/detail?id=20) diff --git a/src/core/lombok/NoArgsConstructor.java b/src/core/lombok/NoArgsConstructor.java index 392fef8b..4662cb5f 100644 --- a/src/core/lombok/NoArgsConstructor.java +++ b/src/core/lombok/NoArgsConstructor.java @@ -48,15 +48,4 @@ public @interface NoArgsConstructor { * Sets the access level of the constructor. By default, generated constructors are {@code public}. */ AccessLevel access() default lombok.AccessLevel.PUBLIC; - - /** - * Constructors are generated with the {@link java.beans.ConstructorProperties} annotation. - * However, this annotation is new in 1.6 which means those compiling for 1.5 will need - * to set this value to true. - * - * @deprecated Java 1.5 has already been end-of-lifed. As java 1.5 loses ground this - * method will eventually be removed. - */ - @Deprecated - boolean suppressConstructorProperties() default false; } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index de6bb048..c780d4ef 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -26,6 +26,7 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -45,6 +46,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; @@ -54,10 +56,13 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +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; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; @@ -70,11 +75,12 @@ public class HandleConstructor { public static class HandleNoArgsConstructor implements EclipseAnnotationHandler<NoArgsConstructor> { @Override public boolean handle(AnnotationValues<NoArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { EclipseNode typeNode = annotationNode.up(); - List<EclipseNode> fields = new ArrayList<EclipseNode>(); NoArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, ast); + if (level == AccessLevel.NONE) return true; + List<EclipseNode> fields = new ArrayList<EclipseNode>(); + new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, false, ast); return true; } } @@ -83,6 +89,12 @@ public class HandleConstructor { public static class HandleRequiredArgsConstructor implements EclipseAnnotationHandler<RequiredArgsConstructor> { @Override public boolean handle(AnnotationValues<RequiredArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { EclipseNode typeNode = annotationNode.up(); + RequiredArgsConstructor ann = annotation.getInstance(); + AccessLevel level = ann.access(); + String staticName = ann.staticName(); + @SuppressWarnings("deprecation") + boolean suppressConstructorProperties = ann.suppressConstructorProperties(); + if (level == AccessLevel.NONE) return true; List<EclipseNode> fields = new ArrayList<EclipseNode>(); for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; @@ -95,10 +107,7 @@ public class HandleConstructor { boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0; if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child); } - RequiredArgsConstructor ann = annotation.getInstance(); - AccessLevel level = ann.access(); - String staticName = ann.staticName(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, ast); + new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties, ast); return true; } } @@ -107,6 +116,12 @@ public class HandleConstructor { public static class HandleAllArgsConstructor implements EclipseAnnotationHandler<AllArgsConstructor> { @Override public boolean handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { EclipseNode typeNode = annotationNode.up(); + AllArgsConstructor ann = annotation.getInstance(); + AccessLevel level = ann.access(); + String staticName = ann.staticName(); + @SuppressWarnings("deprecation") + boolean suppressConstructorProperties = ann.suppressConstructorProperties(); + if (level == AccessLevel.NONE) return true; List<EclipseNode> fields = new ArrayList<EclipseNode>(); for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; @@ -117,15 +132,12 @@ public class HandleConstructor { if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; fields.add(child); } - AllArgsConstructor ann = annotation.getInstance(); - AccessLevel level = ann.access(); - String staticName = ann.staticName(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, ast); + new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties, ast); return true; } } - public void generateConstructor(AccessLevel level, EclipseNode typeNode, List<EclipseNode> fields, String staticName, boolean skipIfConstructorExists, ASTNode source) { + public void generateConstructor(AccessLevel level, EclipseNode typeNode, List<EclipseNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties, ASTNode source) { if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return; if (skipIfConstructorExists) { for (EclipseNode child : typeNode.down()) { @@ -140,7 +152,7 @@ public class HandleConstructor { boolean staticConstrRequired = staticName != null && !staticName.equals(""); - ConstructorDeclaration constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, source); + ConstructorDeclaration constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, suppressConstructorProperties, source); injectMethod(typeNode, constr); if (staticConstrRequired) { MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, fields, source); @@ -148,8 +160,42 @@ public class HandleConstructor { } } + private static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() }; + private static Annotation[] createConstructorProperties(ASTNode source, Annotation[] originalAnnotationArray, Collection<EclipseNode> fields) { + if (fields.isEmpty()) return originalAnnotationArray; + + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long)pS << 32 | pE; + long[] poss = new long[3]; + Arrays.fill(poss, p); + QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(JAVA_BEANS_CONSTRUCTORPROPERTIES, poss); + Eclipse.setGeneratedBy(constructorPropertiesType, source); + SingleMemberAnnotation ann = new SingleMemberAnnotation(constructorPropertiesType, pS); + ann.declarationSourceEnd = pE; + + ArrayInitializer fieldNames = new ArrayInitializer(); + fieldNames.sourceStart = pS; + fieldNames.sourceEnd = pE; + fieldNames.expressions = new Expression[fields.size()]; + + int ctr = 0; + for (EclipseNode field : fields) { + fieldNames.expressions[ctr] = new StringLiteral(field.getName().toCharArray(), pS, pE, 0); + Eclipse.setGeneratedBy(fieldNames.expressions[ctr], source); + ctr++; + } + + ann.memberValue = fieldNames; + Eclipse.setGeneratedBy(ann, source); + Eclipse.setGeneratedBy(ann.memberValue, source); + if (originalAnnotationArray == null) return new Annotation[] { ann }; + Annotation[] newAnnotationArray = Arrays.copyOf(originalAnnotationArray, originalAnnotationArray.length + 1); + newAnnotationArray[originalAnnotationArray.length] = ann; + return newAnnotationArray; + } + private ConstructorDeclaration createConstructor(AccessLevel level, - EclipseNode type, Collection<EclipseNode> fields, ASTNode source) { + EclipseNode type, Collection<EclipseNode> fields, boolean suppressConstructorProperties, ASTNode source) { long p = (long)source.sourceStart << 32 | source.sourceEnd; ConstructorDeclaration constructor = new ConstructorDeclaration( @@ -201,6 +247,11 @@ public class HandleConstructor { nullChecks.addAll(assigns); constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); + + if (!suppressConstructorProperties && level != AccessLevel.PRIVATE) { + constructor.annotations = createConstructorProperties(source, constructor.annotations, fields); + } + return constructor; } diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java index dbbb91f0..ab1d7c28 100644 --- a/src/core/lombok/eclipse/handlers/HandleData.java +++ b/src/core/lombok/eclipse/handlers/HandleData.java @@ -83,7 +83,7 @@ public class HandleData implements EclipseAnnotationHandler<Data> { //for whatever reason, though you can find callers of that one by focusing on the class name itself //and hitting 'find callers'. - new HandleConstructor().generateConstructor(AccessLevel.PUBLIC, typeNode, nodesForConstructor, ann.staticConstructor(), true, ast); + new HandleConstructor().generateConstructor(AccessLevel.PUBLIC, typeNode, nodesForConstructor, ann.staticConstructor(), true, false, ast); for (Map.Entry<EclipseNode, Boolean> field : gettersAndSetters.entrySet()) { new HandleGetter().generateGetterForField(field.getKey(), annotationNode.get(), AccessLevel.PUBLIC, true); diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 2f4a8474..4331761f 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -59,11 +59,12 @@ public class HandleConstructor { @Override public boolean handle(AnnotationValues<NoArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { markAnnotationAsProcessed(annotationNode, NoArgsConstructor.class); JavacNode typeNode = annotationNode.up(); - List<JavacNode> fields = List.nil(); NoArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false); + if (level == AccessLevel.NONE) return true; + List<JavacNode> fields = List.nil(); + new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, false); return true; } } @@ -73,6 +74,12 @@ public class HandleConstructor { @Override public boolean handle(AnnotationValues<RequiredArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { markAnnotationAsProcessed(annotationNode, RequiredArgsConstructor.class); JavacNode typeNode = annotationNode.up(); + RequiredArgsConstructor ann = annotation.getInstance(); + AccessLevel level = ann.access(); + String staticName = ann.staticName(); + @SuppressWarnings("deprecation") + boolean suppressConstructorProperties = ann.suppressConstructorProperties(); + if (level == AccessLevel.NONE) return true; List<JavacNode> fields = List.nil(); for (JavacNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; @@ -86,10 +93,7 @@ public class HandleConstructor { boolean isNonNull = !findAnnotations(child, TransformationsUtil.NON_NULL_PATTERN).isEmpty(); if ((isFinal || isNonNull) && fieldDecl.init == null) fields = fields.append(child); } - RequiredArgsConstructor ann = annotation.getInstance(); - AccessLevel level = ann.access(); - String staticName = ann.staticName(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false); + new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties); return true; } } @@ -99,6 +103,12 @@ public class HandleConstructor { @Override public boolean handle(AnnotationValues<AllArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { markAnnotationAsProcessed(annotationNode, AllArgsConstructor.class); JavacNode typeNode = annotationNode.up(); + AllArgsConstructor ann = annotation.getInstance(); + AccessLevel level = ann.access(); + String staticName = ann.staticName(); + @SuppressWarnings("deprecation") + boolean suppressConstructorProperties = ann.suppressConstructorProperties(); + if (level == AccessLevel.NONE) return true; List<JavacNode> fields = List.nil(); for (JavacNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; @@ -110,15 +120,12 @@ public class HandleConstructor { if ((fieldFlags & Flags.STATIC) != 0) continue; fields = fields.append(child); } - AllArgsConstructor ann = annotation.getInstance(); - AccessLevel level = ann.access(); - String staticName = ann.staticName(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false); + new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties); return true; } } - public void generateConstructor(AccessLevel level, JavacNode typeNode, List<JavacNode> fields, String staticName, boolean skipIfConstructorExists) { + public void generateConstructor(AccessLevel level, JavacNode typeNode, List<JavacNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties) { if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return; if (skipIfConstructorExists) { for (JavacNode child : typeNode.down()) { @@ -133,7 +140,7 @@ public class HandleConstructor { boolean staticConstrRequired = staticName != null && !staticName.equals(""); - JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields); + JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, suppressConstructorProperties); injectMethod(typeNode, constr); if (staticConstrRequired) { JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, fields); @@ -141,7 +148,20 @@ public class HandleConstructor { } } - private JCMethodDecl createConstructor(AccessLevel level, JavacNode typeNode, List<JavacNode> fields) { + private static void addConstructorProperties(JCModifiers mods, JavacNode node, List<JavacNode> fields) { + if (fields.isEmpty()) return; + TreeMaker maker = node.getTreeMaker(); + JCExpression constructorPropertiesType = chainDots(maker, node, "java", "beans", "ConstructorProperties"); + List<JCExpression> fieldNames = List.nil(); + for (JavacNode field : fields) { + fieldNames = fieldNames.append(maker.Literal(field.getName())); + } + JCExpression fieldNamesArray = maker.NewArray(null, List.<JCExpression>nil(), fieldNames); + JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); + mods.annotations = mods.annotations.append(annotation); + } + + private JCMethodDecl createConstructor(AccessLevel level, JavacNode typeNode, List<JavacNode> fields, boolean suppressConstructorProperties) { TreeMaker maker = typeNode.getTreeMaker(); JCClassDecl type = (JCClassDecl) typeNode.get(); @@ -166,6 +186,10 @@ public class HandleConstructor { } JCModifiers mods = maker.Modifiers(toJavacModifier(level)); + if (!suppressConstructorProperties && level != AccessLevel.PRIVATE) { + addConstructorProperties(mods, typeNode, fields); + } + return maker.MethodDef(mods, typeNode.toName("<init>"), null, type.typarams, params, List.<JCExpression>nil(), maker.Block(0L, nullChecks.appendList(assigns)), null); } diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java index 3bdd2dce..e20b230e 100644 --- a/src/core/lombok/javac/handlers/HandleData.java +++ b/src/core/lombok/javac/handlers/HandleData.java @@ -78,7 +78,7 @@ public class HandleData implements JavacAnnotationHandler<Data> { String staticConstructorName = annotation.getInstance().staticConstructor(); - new HandleConstructor().generateConstructor(AccessLevel.PUBLIC, typeNode, nodesForConstructor, staticConstructorName, true); + new HandleConstructor().generateConstructor(AccessLevel.PUBLIC, typeNode, nodesForConstructor, staticConstructorName, true, false); for (Map.Entry<JavacNode, Boolean> field : gettersAndSetters.entrySet()) { new HandleGetter().generateGetterForField(field.getKey(), annotationNode.get(), AccessLevel.PUBLIC, true); |