diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2010-07-22 14:19:23 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2010-07-22 14:19:23 +0200 |
commit | ceda2e5efe229650d4e95de6b8a2632d9f616592 (patch) | |
tree | 1af7964267dc2ce40e0efb02ee767d58303bfab5 /src | |
parent | 868d8b0f93c0801f638b8c5523291aacd35d9ce2 (diff) | |
download | lombok-ceda2e5efe229650d4e95de6b8a2632d9f616592.tar.gz lombok-ceda2e5efe229650d4e95de6b8a2632d9f616592.tar.bz2 lombok-ceda2e5efe229650d4e95de6b8a2632d9f616592.zip |
toString(), equals(), and hashCode() now use getX() instead of x if either it exists OR it will be generated by some other lombok annotation, addressing issue #110.
code deduplication by removing HandleData's scanning for fields, which is now no longer done; the sub-parts of Data (Getter, Setter, RequiredArgsConstructor, etc) take care of it now.
fix for class-level @Getter/@Setter, which used to go for every field. Now they skip the usual fields (static, for setters final, and $ prefixed fields).
Bugfix for @Data not recognizing that it should let field-level @Getter/@Setter take care of generating the getter/setter for multi field declarations (@Getter int x, y);
Diffstat (limited to 'src')
-rw-r--r-- | src/core/lombok/eclipse/EclipseAST.java | 22 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java | 70 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleConstructor.java | 40 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleData.java | 37 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleGetter.java | 72 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleSetter.java | 71 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacAST.java | 19 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleConstructor.java | 42 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleData.java | 37 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleGetter.java | 71 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleSetter.java | 75 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/JavacHandlerUtil.java | 60 |
12 files changed, 365 insertions, 251 deletions
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index b38ee6fc..1af9bc73 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -233,7 +233,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { case STATEMENT: return buildStatement((Statement) node); case ANNOTATION: - return buildAnnotation((Annotation) node); + return buildAnnotation((Annotation) node, false); default: throw new AssertionError("Did not expect to arrive here: " + kind); } @@ -261,7 +261,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { childNodes.addAll(buildFields(type.fields)); childNodes.addAll(buildTypes(type.memberTypes)); childNodes.addAll(buildMethods(type.methods)); - childNodes.addAll(buildAnnotations(type.annotations)); + childNodes.addAll(buildAnnotations(type.annotations, false)); return putInMap(new EclipseNode(this, type, childNodes, Kind.TYPE)); } @@ -282,7 +282,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { if (setAndGetAsHandled(field)) return null; List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); addIfNotNull(childNodes, buildStatement(field.initialization)); - childNodes.addAll(buildAnnotations(field.annotations)); + childNodes.addAll(buildAnnotations(field.annotations, true)); return putInMap(new EclipseNode(this, field, childNodes, Kind.FIELD)); } @@ -302,7 +302,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); childNodes.addAll(buildArguments(method.arguments)); childNodes.addAll(buildStatements(method.statements)); - childNodes.addAll(buildAnnotations(method.annotations)); + childNodes.addAll(buildAnnotations(method.annotations, false)); return putInMap(new EclipseNode(this, method, childNodes, Kind.METHOD)); } @@ -319,19 +319,23 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { if (setAndGetAsHandled(local)) return null; List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); addIfNotNull(childNodes, buildStatement(local.initialization)); - childNodes.addAll(buildAnnotations(local.annotations)); + childNodes.addAll(buildAnnotations(local.annotations, true)); return putInMap(new EclipseNode(this, local, childNodes, kind)); } - private Collection<EclipseNode> buildAnnotations(Annotation[] annotations) { + private Collection<EclipseNode> buildAnnotations(Annotation[] annotations, boolean varDecl) { List<EclipseNode> elements = new ArrayList<EclipseNode>(); - if (annotations != null) for (Annotation an : annotations) addIfNotNull(elements, buildAnnotation(an)); + if (annotations != null) for (Annotation an : annotations) addIfNotNull(elements, buildAnnotation(an, varDecl)); return elements; } - private EclipseNode buildAnnotation(Annotation annotation) { + private EclipseNode buildAnnotation(Annotation annotation, boolean field) { if (annotation == null) return null; - if (setAndGetAsHandled(annotation)) return null; + boolean handled = setAndGetAsHandled(annotation); + if (!field && handled) { + // @Foo int x, y; is handled in eclipse by putting the same annotation node on 2 FieldDeclarations. + return null; + } return putInMap(new EclipseNode(this, annotation, null, Kind.ANNOTATION)); } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 29e44781..935aee14 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -21,7 +21,7 @@ */ package lombok.eclipse.handlers; -import static lombok.eclipse.Eclipse.fromQualifiedName; +import static lombok.eclipse.Eclipse.*; import java.util.ArrayList; import java.util.Arrays; @@ -29,7 +29,10 @@ import java.util.List; import java.util.regex.Pattern; import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; import lombok.core.handlers.TransformationsUtil; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseNode; @@ -99,7 +102,17 @@ public class EclipseHandlerUtil { } } - private static AbstractMethodDeclaration findGetter(EclipseNode field) { + private static class GetterMethod { + private final char[] name; + private final TypeReference type; + + GetterMethod(char[] name, TypeReference type) { + this.name = name; + this.type = type; + } + } + + private static GetterMethod findGetter(EclipseNode field) { TypeReference fieldType = ((FieldDeclaration)field.get()).type; boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0; EclipseNode typeNode = field.up(); @@ -109,33 +122,68 @@ public class EclipseHandlerUtil { case EXISTS_BY_USER: for (EclipseNode potentialGetter : typeNode.down()) { if (potentialGetter.getKind() != Kind.METHOD) continue; - AbstractMethodDeclaration method = (AbstractMethodDeclaration) potentialGetter.get(); + if (!(potentialGetter.get() instanceof MethodDeclaration)) continue; + MethodDeclaration method = (MethodDeclaration) potentialGetter.get(); + if (!potentialGetterName.equals(new String(method.selector))) continue; /** static getX() methods don't count. */ if ((method.modifiers & ClassFileConstants.AccStatic) != 0) continue; /** Nor do getters with a non-empty parameter list. */ if (method.arguments != null && method.arguments.length > 0) continue; - return method; + return new GetterMethod(method.selector, method.returnType); + } + } + } + + // Check if the field has a @Getter annotation. + + boolean hasGetterAnnotation = false; + + for (EclipseNode child : field.down()) { + if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) { + AnnotationValues<Getter> ann = Eclipse.createAnnotation(Getter.class, child); + if (ann.getInstance().value() == AccessLevel.NONE) return null; //Definitely WONT have a getter. + hasGetterAnnotation = true; + } + } + + // Check if the class has a @Getter annotation. + + if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) { + //Check if the class has @Getter or @Data annotation. + + EclipseNode containingType = field.up(); + if (containingType != null) for (EclipseNode child : containingType.down()) { + if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Data.class, child)) hasGetterAnnotation = true; + if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) { + AnnotationValues<Getter> ann = Eclipse.createAnnotation(Getter.class, child); + if (ann.getInstance().value() == AccessLevel.NONE) return null; //Definitely WONT have a getter. + hasGetterAnnotation = true; } } } + if (hasGetterAnnotation) { + String getterName = TransformationsUtil.toGetterName(field.getName(), isBoolean); + return new GetterMethod(getterName.toCharArray(), fieldType); + } + return null; } static TypeReference getFieldType(EclipseNode field, boolean useFieldsDirectly) { - AbstractMethodDeclaration getter = useFieldsDirectly ? null : findGetter(field); - if (!(getter instanceof MethodDeclaration)) { + GetterMethod getter = useFieldsDirectly ? null : findGetter(field); + if (getter == null) { return ((FieldDeclaration)field.get()).type; } - return ((MethodDeclaration)getter).returnType; + return getter.type; } static Expression createFieldAccessor(EclipseNode field, boolean useFieldsDirectly, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long)pS << 32 | pE; - AbstractMethodDeclaration getter = useFieldsDirectly ? null : findGetter(field); + GetterMethod getter = useFieldsDirectly ? null : findGetter(field); if (getter == null) { FieldReference thisX = new FieldReference(field.getName().toCharArray(), p); @@ -150,7 +198,7 @@ public class EclipseHandlerUtil { call.sourceStart = pS; call.sourceEnd = pE; call.receiver = new ThisReference(pS, pE); Eclipse.setGeneratedBy(call.receiver, source); - call.selector = getter.selector; + call.selector = getter.name; return call; } @@ -158,7 +206,7 @@ public class EclipseHandlerUtil { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long)pS << 32 | pE; - AbstractMethodDeclaration getter = useFieldsDirectly ? null : findGetter(field); + GetterMethod getter = useFieldsDirectly ? null : findGetter(field); if (getter == null) { NameReference ref; @@ -178,7 +226,7 @@ public class EclipseHandlerUtil { call.sourceStart = pS; call.sourceEnd = pE; call.receiver = new SingleNameReference(receiver, p); Eclipse.setGeneratedBy(call.receiver, source); - call.selector = getter.selector; + call.selector = getter.name; return call; } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index c780d4ef..6c857c34 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -80,7 +80,7 @@ public class HandleConstructor { String staticName = ann.staticName(); if (level == AccessLevel.NONE) return true; List<EclipseNode> fields = new ArrayList<EclipseNode>(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, false, ast); + new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, false, ast); return true; } } @@ -95,23 +95,27 @@ public class HandleConstructor { @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; - FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - //Skip fields that start with $ - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; - //Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; - boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; - boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0; - if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child); - } - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties, ast); + new HandleConstructor().generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, false, suppressConstructorProperties, ast); return true; } } + private static List<EclipseNode> findRequiredFields(EclipseNode typeNode) { + List<EclipseNode> fields = new ArrayList<EclipseNode>(); + for (EclipseNode child : typeNode.down()) { + if (child.getKind() != Kind.FIELD) continue; + FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); + //Skip fields that start with $ + if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; + //Skip static fields. + if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; + boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; + boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0; + if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child); + } + return fields; + } + @ProviderFor(EclipseAnnotationHandler.class) public static class HandleAllArgsConstructor implements EclipseAnnotationHandler<AllArgsConstructor> { @Override public boolean handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { @@ -132,12 +136,16 @@ public class HandleConstructor { if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; fields.add(child); } - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties, ast); + new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, suppressConstructorProperties, ast); return true; } } - public void generateConstructor(AccessLevel level, EclipseNode typeNode, List<EclipseNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties, ASTNode source) { + public void generateRequiredArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, ASTNode source) { + generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, source); + } + + public void generateConstructor(EclipseNode typeNode, AccessLevel level, 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()) { diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java index ab1d7c28..0a28ccf4 100644 --- a/src/core/lombok/eclipse/handlers/HandleData.java +++ b/src/core/lombok/eclipse/handlers/HandleData.java @@ -21,23 +21,13 @@ */ package lombok.eclipse.handlers; -import static lombok.eclipse.handlers.EclipseHandlerUtil.findAnnotations; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - import lombok.AccessLevel; import lombok.Data; -import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.handlers.TransformationsUtil; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.mangosdk.spi.ProviderFor; @@ -62,37 +52,18 @@ public class HandleData implements EclipseAnnotationHandler<Data> { return false; } - List<EclipseNode> nodesForConstructor = new ArrayList<EclipseNode>(); - Map<EclipseNode, Boolean> gettersAndSetters = new LinkedHashMap<EclipseNode, Boolean>(); - for (EclipseNode child : typeNode.down()) { - if (child.getKind() != Kind.FIELD) continue; - FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - //Skip fields that start with $ - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; - //Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; - boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; - boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0; - if ((isFinal || isNonNull) && fieldDecl.initialization == null) nodesForConstructor.add(child); - gettersAndSetters.put(child, !isFinal); - } - //Careful: Generate the public static constructor (if there is one) LAST, so that any attempt to //'find callers' on the annotation node will find callers of the constructor, which is by far the //most useful of the many methods built by @Data. This trick won't work for the non-static constructor, //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, false, ast); - - for (Map.Entry<EclipseNode, Boolean> field : gettersAndSetters.entrySet()) { - new HandleGetter().generateGetterForField(field.getKey(), annotationNode.get(), AccessLevel.PUBLIC, true); - if (field.getValue()) new HandleSetter().generateSetterForField(field.getKey(), annotationNode.get(), AccessLevel.PUBLIC, true); - } - + new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); new HandleToString().generateToStringForType(typeNode, annotationNode); - + new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), true, ast); + return false; } } diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 70423fbf..597e05ca 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -53,6 +53,45 @@ import org.mangosdk.spi.ProviderFor; */ @ProviderFor(EclipseAnnotationHandler.class) public class HandleGetter implements EclipseAnnotationHandler<Getter> { + public boolean generateGetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelGetter) { + if (checkForTypeLevelGetter) { + if (typeNode != null) for (EclipseNode child : typeNode.down()) { + if (child.getKind() == Kind.ANNOTATION) { + if (annotationTypeMatches(Getter.class, child)) { + //The annotation will make it happen, so we can skip it. + return true; + } + } + } + } + + TypeDeclaration typeDecl = null; + if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); + int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; + boolean notAClass = (modifiers & + (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; + + if (typeDecl == null || notAClass) { + pos.addError("@Getter is only supported on a class or a field."); + return false; + } + + for (EclipseNode field : typeNode.down()) { + if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, pos.get(), level); + } + return true; + } + + public boolean fieldQualifiesForGetterGeneration(EclipseNode field) { + if (field.getKind() != Kind.FIELD) return false; + FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); + //Skip fields that start with $ + if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') return false; + //Skip static fields. + if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) return false; + return true; + } + /** * Generates a getter on the stated field. * @@ -65,7 +104,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { * If not, the getter is still generated if it isn't already there, though there will not * be a warning if its already there. The default access level is used. */ - public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, boolean checkForTypeLevelGetter) { + public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level) { for (EclipseNode child : fieldNode.down()) { if (child.getKind() == Kind.ANNOTATION) { if (annotationTypeMatches(Getter.class, child)) { @@ -75,18 +114,6 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { } } - if (checkForTypeLevelGetter) { - EclipseNode containingType = fieldNode.up(); - if (containingType != null) for (EclipseNode child : containingType.down()) { - if (child.getKind() == Kind.ANNOTATION) { - if (annotationTypeMatches(Getter.class, child)) { - //The annotation will make it happen, so we can skip it. - return; - } - } - } - } - createGetterForField(level, fieldNode, fieldNode, pos, false); } @@ -100,22 +127,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { return createGetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true); } if (node.getKind() == Kind.TYPE) { - TypeDeclaration typeDecl = null; - if (node.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) node.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; - - if (typeDecl == null || notAClass) { - annotationNode.addError("@Getter is only supported on a class."); - return false; - } - - for (EclipseNode field : node.down()) { - if (field.getKind() != Kind.FIELD) continue; - generateGetterForField(field, ast, level, false); - } - return true; + return generateGetterForType(node, annotationNode, level, false); } return false; } @@ -130,7 +142,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { private boolean createGetterForField(AccessLevel level, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { if (fieldNode.getKind() != Kind.FIELD) { - errorNode.addError("@Getter is only supported on a field."); + errorNode.addError("@Getter is only supported on a class or a field."); return true; } diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index ccdbbb2c..1583de1b 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -58,6 +58,44 @@ import org.mangosdk.spi.ProviderFor; */ @ProviderFor(EclipseAnnotationHandler.class) public class HandleSetter implements EclipseAnnotationHandler<Setter> { + public boolean generateSetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelSetter) { + if (checkForTypeLevelSetter) { + if (typeNode != null) for (EclipseNode child : typeNode.down()) { + if (child.getKind() == Kind.ANNOTATION) { + if (annotationTypeMatches(Setter.class, child)) { + //The annotation will make it happen, so we can skip it. + return true; + } + } + } + } + + TypeDeclaration typeDecl = null; + if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); + int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; + boolean notAClass = (modifiers & + (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; + + if (typeDecl == null || notAClass) { + pos.addError("@Setter is only supported on a class or a field."); + return false; + } + + for (EclipseNode field : typeNode.down()) { + if (field.getKind() != Kind.FIELD) continue; + FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); + //Skip fields that start with $ + if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; + //Skip static fields. + if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; + //Skip final fields. + if ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) continue; + + generateSetterForField(field, pos.get(), level); + } + return true; + } + /** * Generates a setter on the stated field. * @@ -70,7 +108,7 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { * If not, the setter is still generated if it isn't already there, though there will not * be a warning if its already there. The default access level is used. */ - public void generateSetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, boolean checkForTypeLevelSetter) { + public void generateSetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level) { for (EclipseNode child : fieldNode.down()) { if (child.getKind() == Kind.ANNOTATION) { if (annotationTypeMatches(Setter.class, child)) { @@ -80,18 +118,6 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { } } - if (checkForTypeLevelSetter) { - EclipseNode containingType = fieldNode.up(); - if (containingType != null) for (EclipseNode child : containingType.down()) { - if (child.getKind() == Kind.ANNOTATION) { - if (annotationTypeMatches(Setter.class, child)) { - //The annotation will make it happen, so we can skip it. - return; - } - } - } - } - createSetterForField(level, fieldNode, fieldNode, pos, false); } @@ -105,22 +131,7 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { return createSetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true); } if (node.getKind() == Kind.TYPE) { - TypeDeclaration typeDecl = null; - if (node.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) node.get(); - int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; - boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; - - if (typeDecl == null || notAClass) { - annotationNode.addError("@Setter is only supported on a class."); - return false; - } - - for (EclipseNode field : node.down()) { - if (field.getKind() != Kind.FIELD) continue; - generateSetterForField(field, ast, level, false); - } - return true; + return generateSetterForType(node, annotationNode, level, false); } return false; } @@ -135,7 +146,7 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { private boolean createSetterForField(AccessLevel level, EclipseNode fieldNode, EclipseNode errorNode, ASTNode pos, boolean whineIfExists) { if (fieldNode.getKind() != Kind.FIELD) { - errorNode.addError("@Setter is only supported on a field."); + errorNode.addError("@Setter is only supported on a class or a field."); return true; } diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index 0edef379..fb837255 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -155,7 +155,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { case STATEMENT: return buildStatementOrExpression(node); case ANNOTATION: - return buildAnnotation((JCAnnotation) node); + return buildAnnotation((JCAnnotation) node, false); default: throw new AssertionError("Did not expect: " + kind); } @@ -176,7 +176,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { if (setAndGetAsHandled(type)) return null; List<JavacNode> childNodes = new ArrayList<JavacNode>(); - for (JCAnnotation annotation : type.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation)); + for (JCAnnotation annotation : type.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, false)); for (JCTree def : type.defs) { /* A def can be: * JCClassDecl for inner types @@ -196,7 +196,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { private JavacNode buildField(JCVariableDecl field) { if (setAndGetAsHandled(field)) return null; List<JavacNode> childNodes = new ArrayList<JavacNode>(); - for (JCAnnotation annotation : field.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation)); + for (JCAnnotation annotation : field.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, true)); addIfNotNull(childNodes, buildExpression(field.init)); return putInMap(new JavacNode(this, field, childNodes, Kind.FIELD)); } @@ -204,7 +204,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { private JavacNode buildLocalVar(JCVariableDecl local, Kind kind) { if (setAndGetAsHandled(local)) return null; List<JavacNode> childNodes = new ArrayList<JavacNode>(); - for (JCAnnotation annotation : local.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation)); + for (JCAnnotation annotation : local.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, true)); addIfNotNull(childNodes, buildExpression(local.init)); return putInMap(new JavacNode(this, local, childNodes, kind)); } @@ -219,7 +219,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { private JavacNode buildMethod(JCMethodDecl method) { if (setAndGetAsHandled(method)) return null; List<JavacNode> childNodes = new ArrayList<JavacNode>(); - for (JCAnnotation annotation : method.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation)); + for (JCAnnotation annotation : method.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, false)); for (JCVariableDecl param : method.params) addIfNotNull(childNodes, buildLocalVar(param, Kind.ARGUMENT)); if (method.body != null && method.body.stats != null) { for (JCStatement statement : method.body.stats) addIfNotNull(childNodes, buildStatement(statement)); @@ -227,8 +227,13 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return putInMap(new JavacNode(this, method, childNodes, Kind.METHOD)); } - private JavacNode buildAnnotation(JCAnnotation annotation) { - if (setAndGetAsHandled(annotation)) return null; + private JavacNode buildAnnotation(JCAnnotation annotation, boolean varDecl) { + boolean handled = setAndGetAsHandled(annotation); + if (!varDecl && handled) { + // @Foo int x, y; is handled in javac by putting the same annotation node on 2 JCVariableDecls. + return null; + } + return putInMap(new JavacNode(this, annotation, null, Kind.ANNOTATION)); } diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index fe5f8566..e24c19d1 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -65,7 +65,7 @@ public class HandleConstructor { String staticName = ann.staticName(); if (level == AccessLevel.NONE) return true; List<JavacNode> fields = List.nil(); - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, false); + new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, false); return true; } } @@ -82,24 +82,28 @@ public class HandleConstructor { @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; - JCVariableDecl fieldDecl = (JCVariableDecl) child.get(); - //Skip fields that start with $ - if (fieldDecl.name.toString().startsWith("$")) continue; - long fieldFlags = fieldDecl.mods.flags; - //Skip static fields. - if ((fieldFlags & Flags.STATIC) != 0) continue; - boolean isFinal = (fieldFlags & Flags.FINAL) != 0; - boolean isNonNull = !findAnnotations(child, TransformationsUtil.NON_NULL_PATTERN).isEmpty(); - if ((isFinal || isNonNull) && fieldDecl.init == null) fields = fields.append(child); - } - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties); + new HandleConstructor().generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, false, suppressConstructorProperties); return true; } } + private static List<JavacNode> findRequiredFields(JavacNode typeNode) { + List<JavacNode> fields = List.nil(); + for (JavacNode child : typeNode.down()) { + if (child.getKind() != Kind.FIELD) continue; + JCVariableDecl fieldDecl = (JCVariableDecl) child.get(); + //Skip fields that start with $ + if (fieldDecl.name.toString().startsWith("$")) continue; + long fieldFlags = fieldDecl.mods.flags; + //Skip static fields. + if ((fieldFlags & Flags.STATIC) != 0) continue; + boolean isFinal = (fieldFlags & Flags.FINAL) != 0; + boolean isNonNull = !findAnnotations(child, TransformationsUtil.NON_NULL_PATTERN).isEmpty(); + if ((isFinal || isNonNull) && fieldDecl.init == null) fields = fields.append(child); + } + return fields; + } + @ProviderFor(JavacAnnotationHandler.class) public static class HandleAllArgsConstructor implements JavacAnnotationHandler<AllArgsConstructor> { @Override public boolean handle(AnnotationValues<AllArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { @@ -123,12 +127,16 @@ public class HandleConstructor { if ((fieldFlags & Flags.STATIC) != 0) continue; fields = fields.append(child); } - new HandleConstructor().generateConstructor(level, typeNode, fields, staticName, false, suppressConstructorProperties); + new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, suppressConstructorProperties); return true; } } - public void generateConstructor(AccessLevel level, JavacNode typeNode, List<JavacNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties) { + public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists) { + generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false); + } + + public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JavacNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties) { if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return; if (skipIfConstructorExists) { for (JavacNode child : typeNode.down()) { diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java index e20b230e..2087c133 100644 --- a/src/core/lombok/javac/handlers/HandleData.java +++ b/src/core/lombok/javac/handlers/HandleData.java @@ -21,16 +21,10 @@ */ package lombok.javac.handlers; -import static lombok.javac.handlers.JavacHandlerUtil.*; - -import java.util.LinkedHashMap; -import java.util.Map; - +import static lombok.javac.handlers.JavacHandlerUtil.markAnnotationAsProcessed; import lombok.AccessLevel; import lombok.Data; -import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.handlers.TransformationsUtil; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -39,8 +33,6 @@ import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCClassDecl; -import com.sun.tools.javac.tree.JCTree.JCVariableDecl; -import com.sun.tools.javac.util.List; /** * Handles the {@code lombok.Data} annotation for javac. @@ -60,31 +52,12 @@ public class HandleData implements JavacAnnotationHandler<Data> { return false; } - List<JavacNode> nodesForConstructor = List.nil(); - Map<JavacNode, Boolean> gettersAndSetters = new LinkedHashMap<JavacNode, Boolean>(); - for (JavacNode child : typeNode.down()) { - if (child.getKind() != Kind.FIELD) continue; - JCVariableDecl fieldDecl = (JCVariableDecl) child.get(); - //Skip fields that start with $ - if (fieldDecl.name.toString().startsWith("$")) continue; - long fieldFlags = fieldDecl.mods.flags; - //Skip static fields. - if ((fieldFlags & Flags.STATIC) != 0) continue; - boolean isFinal = (fieldFlags & Flags.FINAL) != 0; - boolean isNonNull = !findAnnotations(child, TransformationsUtil.NON_NULL_PATTERN).isEmpty(); - if ((isFinal || isNonNull) && fieldDecl.init == null) nodesForConstructor = nodesForConstructor.append(child); - gettersAndSetters.put(child, !isFinal); - } - String staticConstructorName = annotation.getInstance().staticConstructor(); - 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); - if (field.getValue()) new HandleSetter().generateSetterForField(field.getKey(), annotationNode.get(), AccessLevel.PUBLIC, true); - } - + // TODO move this to the end OR move it to the top in eclipse. + new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true); + new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); new HandleToString().generateToStringForType(typeNode, annotationNode); diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 582aa02b..28402346 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -56,6 +56,45 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; */ @ProviderFor(JavacAnnotationHandler.class) public class HandleGetter implements JavacAnnotationHandler<Getter> { + public boolean generateGetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelGetter) { + if (checkForTypeLevelGetter) { + if (typeNode != null) for (JavacNode child : typeNode.down()) { + if (child.getKind() == Kind.ANNOTATION) { + if (Javac.annotationTypeMatches(Getter.class, child)) { + //The annotation will make it happen, so we can skip it. + return true; + } + } + } + } + + JCClassDecl typeDecl = null; + if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); + long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; + boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; + + if (typeDecl == null || notAClass) { + errorNode.addError("@Getter is only supported on a class or a field."); + return false; + } + + for (JavacNode field : typeNode.down()) { + if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, errorNode.get(), level); + } + + return true; + } + + public boolean fieldQualifiesForGetterGeneration(JavacNode field) { + if (field.getKind() != Kind.FIELD) return false; + JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); + //Skip fields that start with $ + if (fieldDecl.name.toString().startsWith("$")) return false; + //Skip static fields. + if ((fieldDecl.mods.flags & Flags.STATIC) != 0) return false; + return true; + } + /** * Generates a getter on the stated field. * @@ -71,7 +110,7 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { * @param fieldNode The node representing the field you want a getter for. * @param pos The node responsible for generating the getter (the {@code @Data} or {@code @Getter} annotation). */ - public void generateGetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean checkForTypeLevelGetter) { + public void generateGetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level) { for (JavacNode child : fieldNode.down()) { if (child.getKind() == Kind.ANNOTATION) { if (Javac.annotationTypeMatches(Getter.class, child)) { @@ -81,18 +120,6 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { } } - if (checkForTypeLevelGetter) { - JavacNode containingType = fieldNode.up(); - if (containingType != null) for (JavacNode child : containingType.down()) { - if (child.getKind() == Kind.ANNOTATION) { - if (Javac.annotationTypeMatches(Getter.class, child)) { - //The annotation will make it happen, so we can skip it. - return; - } - } - } - } - createGetterForField(level, fieldNode, fieldNode, false); } @@ -109,21 +136,7 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { return createGetterForFields(level, fields, annotationNode, true); } if (node.getKind() == Kind.TYPE) { - JCClassDecl typeDecl = null; - if (node.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) node.get(); - long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; - - if (typeDecl == null || notAClass) { - annotationNode.addError("@Getter is only supported on a class."); - return false; - } - - for (JavacNode field : node.down()) { - if (field.getKind() != Kind.FIELD) continue; - generateGetterForField(field, ast, level, false); - } - return true; + return generateGetterForType(node, annotationNode, level, false); } return false; } @@ -139,7 +152,7 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { private boolean createGetterForField(AccessLevel level, JavacNode fieldNode, JavacNode errorNode, boolean whineIfExists) { if (fieldNode.getKind() != Kind.FIELD) { - errorNode.addError("@Getter is only supported on a field."); + errorNode.addError("@Getter is only supported on a class or a field."); return true; } diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 8728ceb3..0c248527 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -32,8 +32,8 @@ import javax.lang.model.type.TypeVisitor; import lombok.AccessLevel; import lombok.Setter; -import lombok.core.AnnotationValues; import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; import lombok.core.handlers.TransformationsUtil; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; @@ -44,7 +44,6 @@ import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.TypeTags; -import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCBlock; @@ -55,15 +54,53 @@ import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Name; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; /** * Handles the {@code lombok.Setter} annotation for javac. */ @ProviderFor(JavacAnnotationHandler.class) public class HandleSetter implements JavacAnnotationHandler<Setter> { + public boolean generateSetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelSetter) { + if (checkForTypeLevelSetter) { + if (typeNode != null) for (JavacNode child : typeNode.down()) { + if (child.getKind() == Kind.ANNOTATION) { + if (Javac.annotationTypeMatches(Setter.class, child)) { + //The annotation will make it happen, so we can skip it. + return true; + } + } + } + } + + JCClassDecl typeDecl = null; + if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); + long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; + boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; + + if (typeDecl == null || notAClass) { + errorNode.addError("@Setter is only supported on a class or a field."); + return false; + } + + for (JavacNode field : typeNode.down()) { + if (field.getKind() != Kind.FIELD) continue; + JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); + //Skip fields that start with $ + if (fieldDecl.name.toString().startsWith("$")) continue; + //Skip static fields. + if ((fieldDecl.mods.flags & Flags.STATIC) != 0) continue; + //Skip final fields. + if ((fieldDecl.mods.flags & Flags.FINAL) != 0) continue; + + generateSetterForField(field, errorNode.get(), level); + } + return true; + } + /** * Generates a setter on the stated field. * @@ -79,7 +116,7 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { * @param fieldNode The node representing the field you want a setter for. * @param pos The node responsible for generating the setter (the {@code @Data} or {@code @Setter} annotation). */ - public void generateSetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean checkForTypeLevelSetter) { + public void generateSetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level) { for (JavacNode child : fieldNode.down()) { if (child.getKind() == Kind.ANNOTATION) { if (Javac.annotationTypeMatches(Setter.class, child)) { @@ -89,18 +126,6 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { } } - if (checkForTypeLevelSetter) { - JavacNode containingType = fieldNode.up(); - if (containingType != null) for (JavacNode child : containingType.down()) { - if (child.getKind() == Kind.ANNOTATION) { - if (Javac.annotationTypeMatches(Setter.class, child)) { - //The annotation will make it happen, so we can skip it. - return; - } - } - } - } - createSetterForField(level, fieldNode, fieldNode, false); } @@ -118,21 +143,7 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { return createSetterForFields(level, fields, annotationNode, true); } if (node.getKind() == Kind.TYPE) { - JCClassDecl typeDecl = null; - if (node.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) node.get(); - long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; - - if (typeDecl == null || notAClass) { - annotationNode.addError("@Setter is only supported on a class."); - return false; - } - - for (JavacNode field : node.down()) { - if (field.getKind() != Kind.FIELD) continue; - generateSetterForField(field, ast, level, false); - } - return true; + return generateSetterForType(node, annotationNode, level, false); } return false; } @@ -148,7 +159,7 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> { private boolean createSetterForField(AccessLevel level, JavacNode fieldNode, JavacNode errorNode, boolean whineIfExists) { if (fieldNode.getKind() != Kind.FIELD) { - fieldNode.addError("@Setter is only supported on a field."); + fieldNode.addError("@Setter is only supported on a class or a field."); return true; } diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index cb2697f1..09d5c3fe 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -21,12 +21,18 @@ */ package lombok.javac.handlers; +import static lombok.javac.Javac.annotationTypeMatches; + import java.lang.annotation.Annotation; import java.util.regex.Pattern; import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.core.AnnotationValues; import lombok.core.AST.Kind; import lombok.core.handlers.TransformationsUtil; +import lombok.javac.Javac; import lombok.javac.JavacNode; import com.sun.tools.javac.code.Flags; @@ -270,7 +276,17 @@ public class JavacHandlerUtil { } } - private static JCMethodDecl findGetter(JavacNode field) { + private static class GetterMethod { + private final Name name; + private final JCExpression type; + + GetterMethod(Name name, JCExpression type) { + this.name = name; + this.type = type; + } + } + + private static GetterMethod findGetter(JavacNode field) { JCVariableDecl decl = (JCVariableDecl)field.get(); JavacNode typeNode = field.up(); for (String potentialGetterName : toAllGetterNames(decl)) { @@ -280,15 +296,49 @@ public class JavacHandlerUtil { for (JavacNode potentialGetter : typeNode.down()) { if (potentialGetter.getKind() != Kind.METHOD) continue; JCMethodDecl method = (JCMethodDecl) potentialGetter.get(); + if (!method.name.contentEquals(potentialGetterName)) continue; /** static getX() methods don't count. */ if ((method.mods.flags & Flags.STATIC) != 0) continue; /** Nor do getters with a non-empty parameter list. */ if (method.params != null && method.params.size() > 0) continue; - return method; + return new GetterMethod(method.name, method.restype); + } + } + } + + // Check if the field has a @Getter annotation. + + boolean hasGetterAnnotation = false; + + for (JavacNode child : field.down()) { + if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) { + AnnotationValues<Getter> ann = Javac.createAnnotation(Getter.class, child); + if (ann.getInstance().value() == AccessLevel.NONE) return null; //Definitely WONT have a getter. + hasGetterAnnotation = true; + } + } + + // Check if the class has a @Getter annotation. + + if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) { + //Check if the class has @Getter or @Data annotation. + + JavacNode containingType = field.up(); + if (containingType != null) for (JavacNode child : containingType.down()) { + if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Data.class, child)) hasGetterAnnotation = true; + if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) { + AnnotationValues<Getter> ann = Javac.createAnnotation(Getter.class, child); + if (ann.getInstance().value() == AccessLevel.NONE) return null; //Definitely WONT have a getter. + hasGetterAnnotation = true; } } } + if (hasGetterAnnotation) { + String getterName = toGetterName(decl); + return new GetterMethod(field.toName(getterName), decl.vartype); + } + return null; } @@ -298,13 +348,13 @@ public class JavacHandlerUtil { * @see #createFieldAccessor(TreeMaker, JavacNode) */ static JCExpression getFieldType(JavacNode field, boolean useFieldsDirectly) { - JCMethodDecl getter = useFieldsDirectly ? null : findGetter(field); + GetterMethod getter = useFieldsDirectly ? null : findGetter(field); if (getter == null) { return ((JCVariableDecl)field.get()).vartype; } - return getter.restype; + return getter.type; } /** @@ -315,7 +365,7 @@ public class JavacHandlerUtil { } static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, boolean useFieldsDirectly, JCExpression receiver) { - JCMethodDecl getter = useFieldsDirectly ? null : findGetter(field); + GetterMethod getter = useFieldsDirectly ? null : findGetter(field); if (getter == null) { return maker.Select(receiver, ((JCVariableDecl)field.get()).name); |