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/core/lombok/eclipse/handlers | |
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/core/lombok/eclipse/handlers')
5 files changed, 170 insertions, 120 deletions
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; } |