Added support for @ConstructorProperties generation for generated constructors.

Also added fix: @Constructor with access level none are now no longer generated.

Implements issue #122
This commit is contained in:
Reinier Zwitserloot committed 2010-07-19 23:48:12 +02:00
1 parent 734baebfa9
commit 5c6fe62a85
6 files changed
+104 -39

No files matched your search

+1
View File
@@ -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)
-11
View File
@@ -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;
}
@@ -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;
}
@@ -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);
@@ -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);
}
@@ -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);