From 649a7b72bf8119e286e991bce29fb63bdb26c09d Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 15 May 2018 00:50:58 +0200 Subject: [new-style include/exclude] added new-style include/exclude support to EqualsAndHashCode. --- src/core/lombok/EqualsAndHashCode.java | 29 ++++- src/core/lombok/ToString.java | 4 +- src/core/lombok/core/AnnotationValues.java | 57 +++++++++ .../core/handlers/InclusionExclusionUtils.java | 79 +++++++----- .../eclipse/handlers/EclipseHandlerUtil.java | 14 +++ .../lombok/eclipse/handlers/HandleBuilder.java | 7 +- .../eclipse/handlers/HandleEqualsAndHashCode.java | 140 ++++++++------------- .../lombok/eclipse/handlers/HandleToString.java | 14 +-- src/core/lombok/javac/handlers/HandleBuilder.java | 7 +- .../javac/handlers/HandleEqualsAndHashCode.java | 111 ++++++---------- src/core/lombok/javac/handlers/HandleToString.java | 14 +-- .../lombok/javac/handlers/JavacHandlerUtil.java | 11 +- 12 files changed, 265 insertions(+), 222 deletions(-) (limited to 'src') diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java index 2f88ac50..b2fc672d 100644 --- a/src/core/lombok/EqualsAndHashCode.java +++ b/src/core/lombok/EqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,6 +37,8 @@ public @interface EqualsAndHashCode { /** * Any fields listed here will not be taken into account in the generated {@code equals} and {@code hashCode} implementations. * Mutually exclusive with {@link #of()}. + *

+ * Will soon be marked {@code @Deprecated}; use the {@code @EqualsAndHashCode.Exclude} annotation instead. * * @return A list of fields to exclude. */ @@ -47,6 +49,8 @@ public @interface EqualsAndHashCode { * Normally, all non-static, non-transient fields are used for identity. *

* Mutually exclusive with {@link #exclude()}. + *

+ * Will soon be marked {@code @Deprecated}; use the {@code @EqualsAndHashCode.Include} annotation together with {@code @EqualsAndHashCode(onlyExplicitlyIncluded = true)}. * * @return A list of fields to use (default: all of them). */ @@ -89,4 +93,27 @@ public @interface EqualsAndHashCode { @Retention(RetentionPolicy.SOURCE) @Target({}) @interface AnyAnnotation {} + + /** + * Only include fields and methods explicitly marked with {@code @EqualsAndHashCode.Include}. + * Normally, all (non-static, non-transient) fields are included by default. + */ + boolean onlyExplicitlyIncluded() default false; + + /** + * If present, do not include this field in the generated {@code equals} and {@code hashCode} methods. + */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface Exclude {} + + /** + * Configure the behaviour of how this member is treated in the {@code equals} and {@code hashCode} implementation; if on a method, include the method's return value as part of calculating hashCode/equality. + */ + @Target({ElementType.FIELD, ElementType.METHOD}) + @Retention(RetentionPolicy.SOURCE) + public @interface Include { + /** Defaults to the method name of the annotated member. If on a method and the name equals the name of a default-included field, this member takes its place. */ + String replaces() default ""; + } } diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java index 218e4c00..ae3f071f 100644 --- a/src/core/lombok/ToString.java +++ b/src/core/lombok/ToString.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -58,7 +58,7 @@ public @interface ToString { *

* Mutually exclusive with {@link #exclude()}. *

- * Will soon be marked {@code @Deprecated}; use the {@code @ToString.Only} annotation instead. + * Will soon be marked {@code @Deprecated}; use the {@code @ToString.Include} annotation together with {@code @ToString(onlyExplicitlyIncluded = true)}. * * @return A list of fields to use (default: all of them). */ diff --git a/src/core/lombok/core/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java index df18ec30..b6e78de4 100644 --- a/src/core/lombok/core/AnnotationValues.java +++ b/src/core/lombok/core/AnnotationValues.java @@ -27,6 +27,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -159,6 +160,62 @@ public class AnnotationValues { private A cachedInstance = null; + public List getAsStringList(String methodName) { + AnnotationValue v = values.get(methodName); + + if (v == null) { + String[] s = getDefaultIf(methodName, String[].class, new String[0]); + return Collections.unmodifiableList(Arrays.asList(s)); + } + + List out = new ArrayList(v.valueGuesses.size()); + int idx = 0; + for (Object guess : v.valueGuesses) { + Object result = guess == null ? null : guessToType(guess, String.class, v, idx); + if (result == null) { + if (v.valueGuesses.size() == 1) { + String[] s = getDefaultIf(methodName, String[].class, new String[0]); + return Collections.unmodifiableList(Arrays.asList(s)); + } + throw new AnnotationValueDecodeFail(v, + "I can't make sense of this annotation value. Try using a fully qualified literal.", idx); + } + out.add((String) result); + } + + return Collections.unmodifiableList(out); + } + + public String getAsString(String methodName) { + AnnotationValue v = values.get(methodName); + if (v == null || v.valueGuesses.size() != 1) { + return getDefaultIf(methodName, String.class, ""); + } + + Object guess = guessToType(v.valueGuesses.get(0), String.class, v, 0); + if (guess instanceof String) return (String) guess; + return getDefaultIf(methodName, String.class, ""); + } + + public boolean getAsBoolean(String methodName) { + AnnotationValue v = values.get(methodName); + if (v == null || v.valueGuesses.size() != 1) { + return getDefaultIf(methodName, Boolean.class, false); + } + + Object guess = guessToType(v.valueGuesses.get(0), Boolean.class, v, 0); + if (guess instanceof Boolean) return ((Boolean) guess).booleanValue(); + return getDefaultIf(methodName, Boolean.class, false); + } + + public T getDefaultIf(String methodName, Class type, T defaultValue) { + try { + return type.cast(type.getMethod(methodName).getDefaultValue()); + } catch (Exception e) { + return defaultValue; + } + } + /** * Creates an actual annotation instance. You can use this to query any annotation methods, except for * those annotation methods with class literals, as those can most likely not be turned into Class objects. diff --git a/src/core/lombok/core/handlers/InclusionExclusionUtils.java b/src/core/lombok/core/handlers/InclusionExclusionUtils.java index e2f686cd..8d6c717f 100644 --- a/src/core/lombok/core/handlers/InclusionExclusionUtils.java +++ b/src/core/lombok/core/handlers/InclusionExclusionUtils.java @@ -21,15 +21,15 @@ */ package lombok.core.handlers; +import java.lang.annotation.Annotation; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import lombok.EqualsAndHashCode; import lombok.ToString; -import lombok.ToString.Include; import lombok.core.AST; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; @@ -61,23 +61,23 @@ public class InclusionExclusionUtils { public static void checkForBogusFieldNames(LombokNode type, AnnotationValues annotation, List excludes, List includes) { if (excludes != null && !excludes.isEmpty()) { for (int i : createListOfNonExistentFields(excludes, type, true, false)) { - annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i); + if (annotation != null) annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i); } } if (includes != null && !includes.isEmpty()) { for (int i : createListOfNonExistentFields(includes, type, false, false)) { - annotation.setWarning("of", "This field does not exist.", i); + if (annotation != null) annotation.setWarning("of", "This field does not exist.", i); } } } - public static class ToStringMember { + public static class Included { private final L node; - private final ToString.Include inc; + private final I inc; private final boolean defaultInclude; - public ToStringMember(L node, ToString.Include inc, boolean defaultInclude) { + public Included(L node, I inc, boolean defaultInclude) { this.node = node; this.inc = inc; this.defaultInclude = defaultInclude; @@ -87,7 +87,7 @@ public class InclusionExclusionUtils { return node; } - public ToString.Include getInc() { + public I getInc() { return inc; } @@ -96,34 +96,45 @@ public class InclusionExclusionUtils { } } - public static , L extends LombokNode, N> List> handleToStringMarking(LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode) { - ToString ann = annotation == null ? null : annotation.getInstance(); - List oldExcludes = (ann != null && annotation.isExplicit("exclude")) ? Arrays.asList(ann.exclude()) : null; - List oldIncludes = (ann != null && annotation.isExplicit("of")) ? Arrays.asList(ann.of()) : null; + private static String innerAnnName(Class type) { + String name = type.getSimpleName(); + Class c = type.getEnclosingClass(); + while (c != null) { + name = c.getSimpleName() + "." + name; + c = c.getEnclosingClass(); + } + return name; + } + + public static , L extends LombokNode, N, I extends Annotation> List> handleIncludeExcludeMarking(Class inclType, String replaceName, Class exclType, LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode) { + List oldExcludes = (annotation != null && annotation.isExplicit("exclude")) ? annotation.getAsStringList("exclude") : null; + List oldIncludes = (annotation != null && annotation.isExplicit("of")) ? annotation.getAsStringList("of") : null; - boolean onlyExplicitlyIncluded = ann != null && ann.onlyExplicitlyIncluded(); + boolean onlyExplicitlyIncluded = annotation != null ? annotation.getAsBoolean("onlyExplicitlyIncluded") : false; boolean memberAnnotationMode = onlyExplicitlyIncluded; - List> members = new ArrayList>(); + List> members = new ArrayList>(); List namesToAutoExclude = new ArrayList(); if (typeNode == null || typeNode.getKind() != Kind.TYPE) return null; checkForBogusFieldNames(typeNode, annotation, oldExcludes, oldIncludes); + String inclTypeName = innerAnnName(inclType); + String exclTypeName = innerAnnName(exclType); if (oldExcludes != null && oldIncludes != null) { oldExcludes = null; - annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored."); + if (annotation != null) annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored."); } for (L child : typeNode.down()) { - boolean markExclude = child.getKind() == Kind.FIELD && child.hasAnnotation(ToString.Exclude.class); - AnnotationValues markInclude = null; - if (child.getKind() == Kind.FIELD || child.getKind() == Kind.METHOD) markInclude = child.findAnnotation(ToString.Include.class); + boolean markExclude = child.getKind() == Kind.FIELD && child.hasAnnotation(exclType); + AnnotationValues markInclude = null; + if (child.getKind() == Kind.FIELD || child.getKind() == Kind.METHOD) markInclude = child.findAnnotation(inclType); if (markExclude || markInclude != null) memberAnnotationMode = true; if (markInclude != null && markExclude) { - child.addError("@ToString.Exclude and @ToString.Include are mutually exclusive; the @Include annotation will be ignored"); + child.addError("@" + exclTypeName + " and @" + inclTypeName + " are mutually exclusive; the @Include annotation will be ignored"); markInclude = null; } @@ -143,36 +154,36 @@ public class InclusionExclusionUtils { if (oldExcludes != null && oldExcludes.contains(name)) continue; if (markInclude != null) { - Include inc = markInclude.getInstance(); + I inc = markInclude.getInstance(); if (child.getKind() == Kind.METHOD) { if (child.countMethodParameters() > 0) { - child.addError("Methods included for @ToString must have no arguments; it will not be included"); + child.addError("Methods included with @" + inclTypeName + " must have no arguments; it will not be included"); continue; } - String n = inc.name(); + String n = replaceName != null ? markInclude.getAsString(replaceName) : ""; if (n.isEmpty()) n = name; namesToAutoExclude.add(n); } - members.add(new ToStringMember(child, inc, false)); + members.add(new Included(child, inc, false)); continue; } if (onlyExplicitlyIncluded) continue; if (oldIncludes != null) { - if (child.getKind() == Kind.FIELD && oldIncludes.contains(name)) members.add(new ToStringMember(child, null, false)); + if (child.getKind() == Kind.FIELD && oldIncludes.contains(name)) members.add(new Included(child, null, false)); continue; } if (child.getKind() != Kind.FIELD) continue; if (child.isStatic()) continue; if (name.startsWith("$")) continue; if (child.isEnumMember()) continue; - members.add(new ToStringMember(child, null, true)); + members.add(new Included(child, null, true)); } /* delete default-included fields with the same name as an explicit inclusion */ { - Iterator> it = members.iterator(); + Iterator> it = members.iterator(); while (it.hasNext()) { - ToStringMember m = it.next(); + Included m = it.next(); if (m.isDefaultInclude() && namesToAutoExclude.contains(m.getNode().getName())) it.remove(); } } @@ -185,8 +196,14 @@ public class InclusionExclusionUtils { return null; } - Collections.sort(members, new Comparator>() { - @Override public int compare(ToStringMember a, ToStringMember b) { + return members; + } + + public static , L extends LombokNode, N> List> handleToStringMarking(LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode) { + List> members = handleIncludeExcludeMarking(ToString.Include.class, "name", ToString.Exclude.class, typeNode, annotation, annotationNode); + + Collections.sort(members, new Comparator>() { + @Override public int compare(Included a, Included b) { int ra = a.getInc() == null ? 0 : a.getInc().rank(); int rb = b.getInc() == null ? 0 : b.getInc().rank(); if (ra < rb) return +1; @@ -203,4 +220,8 @@ public class InclusionExclusionUtils { }); return members; } + + public static , L extends LombokNode, N> List> handleEqualsAndHashCodeMarking(LombokNode typeNode, AnnotationValues annotation, LombokNode annotationNode) { + return handleIncludeExcludeMarking(EqualsAndHashCode.Include.class, "replaces", EqualsAndHashCode.Exclude.class, typeNode, annotation, annotationNode); + } } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 9f9b3975..1758a220 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1041,6 +1041,20 @@ public class EclipseHandlerUtil { return call; } + static Expression createMethodAccessor(EclipseNode method, ASTNode source, char[] receiver) { + int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd; + long p = (long) pS << 32 | pE; + + MethodDeclaration methodDecl = (MethodDeclaration) method.get(); + MessageSend call = new MessageSend(); + setGeneratedBy(call, source); + call.sourceStart = pS; call.statementEnd = call.sourceEnd = pE; + call.receiver = new SingleNameReference(receiver, p); + setGeneratedBy(call.receiver, source); + call.selector = methodDecl.selector; + return call; + } + /** Serves as return value for the methods that check for the existence of fields and methods. */ public enum MemberExistsResult { NOT_EXISTS, EXISTS_BY_LOMBOK, EXISTS_BY_USER; diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index aa9fad84..38bd9b60 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -74,9 +74,10 @@ import lombok.Builder; import lombok.Builder.ObtainVia; import lombok.ConfigurationKeys; import lombok.Singular; +import lombok.ToString; import lombok.core.AST.Kind; import lombok.core.handlers.HandlerUtil; -import lombok.core.handlers.InclusionExclusionUtils.ToStringMember; +import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.eclipse.Eclipse; @@ -450,10 +451,10 @@ public class HandleBuilder extends EclipseAnnotationHandler { } if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { - List> fieldNodes = new ArrayList>(); + List> fieldNodes = new ArrayList>(); for (BuilderFieldData bfd : builderFields) { for (EclipseNode f : bfd.createdFields) { - fieldNodes.add(new ToStringMember(f, null, true)); + fieldNodes.add(new Included(f, null, true)); } } MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD); diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 2a497420..e30df698 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2015 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,6 +38,8 @@ import lombok.ConfigurationKeys; import lombok.EqualsAndHashCode; import lombok.core.AST.Kind; import lombok.core.handlers.HandlerUtil; +import lombok.core.handlers.InclusionExclusionUtils; +import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.core.AnnotationValues; import lombok.core.configuration.CallSuperType; import lombok.eclipse.Eclipse; @@ -56,7 +58,6 @@ import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; -import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression; import org.eclipse.jdt.internal.compiler.ast.IntLiteral; @@ -98,62 +99,41 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet(Arrays.asList( "byte", "short", "int", "long", "char", "boolean", "double", "float"))); - public void checkForBogusFieldNames(EclipseNode type, AnnotationValues annotation) { - if (annotation.isExplicit("exclude")) { - for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().exclude()), type, true, true)) { - annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i); - } - } - if (annotation.isExplicit("of")) { - for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().of()), type, false, false)) { - annotation.setWarning("of", "This field does not exist.", i); - } - } - } - - public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) { - if (hasAnnotation(EqualsAndHashCode.class, typeNode)) { - //The annotation will make it happen, so we can skip it. - return; - } - - Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); - FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - - generateMethods(typeNode, errorNode, null, null, null, false, access, new ArrayList()); - } - @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode"); EqualsAndHashCode ann = annotation.getInstance(); - List excludes = Arrays.asList(ann.exclude()); - List includes = Arrays.asList(ann.of()); - EclipseNode typeNode = annotationNode.up(); + List> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode); + if (members == null) return; List onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode); - checkForBogusFieldNames(typeNode, annotation); Boolean callSuper = ann.callSuper(); if (!annotation.isExplicit("callSuper")) callSuper = null; - if (!annotation.isExplicit("exclude")) excludes = null; - if (!annotation.isExplicit("of")) includes = null; - - if (excludes != null && includes != null) { - excludes = null; - annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored."); - } Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam); + generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, fieldAccess, onParam); + } + + public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) { + if (hasAnnotation(EqualsAndHashCode.class, typeNode)) { + //The annotation will make it happen, so we can skip it. + return; + } + + List> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null); + + Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); + FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; + + generateMethods(typeNode, errorNode, members, null, false, access, new ArrayList()); } - public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List excludes, List includes, - Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List onParam) { - assert excludes == null || includes == null; + public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List> members, + Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List onParam) { TypeDeclaration typeDecl = null; @@ -171,7 +151,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler nodesForEquality = new ArrayList(); - if (includes != null) { - for (EclipseNode child : typeNode.down()) { - if (child.getKind() != Kind.FIELD) continue; - FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - if (includes.contains(new String(fieldDecl.name))) nodesForEquality.add(child); - } - } else { - for (EclipseNode child : typeNode.down()) { - if (child.getKind() != Kind.FIELD) continue; - FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - if (!filterField(fieldDecl)) continue; - - //Skip transient fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0) continue; - //Skip excluded fields. - if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue; - nodesForEquality.add(child); - } - } - boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0; boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject; MemberExistsResult equalsExists = methodExists("equals", typeNode, 1); @@ -257,7 +216,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) { + public MethodDeclaration createHashCode(EclipseNode type, Collection> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long)pS << 32 | pE; - MethodDeclaration method = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); setGeneratedBy(method, source); method.modifiers = toEclipseModifier(AccessLevel.PUBLIC); @@ -294,10 +252,10 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler statements = new ArrayList(); - final boolean isEmpty = fields.isEmpty(); + final boolean isEmpty = members.isEmpty(); /* final int PRIME = X; */ { - /* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */ + /* Without members, PRIME isn't used, as that would trigger a 'local variable not used' warning. */ if (!isEmpty) { LocalDeclaration primeDecl = new LocalDeclaration(PRIME, pS, pE); setGeneratedBy(primeDecl, source); @@ -310,7 +268,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler member : members) { + EclipseNode memberNode = member.getNode(); + boolean isMethod = memberNode.getKind() == Kind.METHOD; + + TypeReference fType = getFieldType(memberNode, fieldAccess); + char[] dollarFieldName = ((isMethod ? "$$" : "$") + memberNode.getName()).toCharArray(); char[] token = fType.getLastToken(); - Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source); + Expression fieldAccessor = isMethod ? createMethodAccessor(memberNode, source) : createFieldAccessor(memberNode, fieldAccess, source); if (fType.dimensions() == 0 && token != null) { if (Arrays.equals(TypeConstants.BOOLEAN, token)) { /* booleanField ? X : Y */ @@ -534,12 +495,11 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List onParam) { + public MethodDeclaration createEquals(EclipseNode type, Collection> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List onParam) { int pS = source.sourceStart; int pE = source.sourceEnd; long p = (long)pS << 32 | pE; - MethodDeclaration method = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); setGeneratedBy(method, source); method.modifiers = toEclipseModifier(AccessLevel.PUBLIC); method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); @@ -606,7 +566,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler other = (Outer.Inner.MyType) o; */ { - if (!fields.isEmpty() || needsCanEqual) { + if (!members.isEmpty() || needsCanEqual) { LocalDeclaration other = new LocalDeclaration(otherName, pS, pE); other.modifiers |= ClassFileConstants.AccFinal; setGeneratedBy(other, source); @@ -675,11 +635,14 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler member : members) { + EclipseNode memberNode = member.getNode(); + boolean isMethod = memberNode.getKind() == Kind.METHOD; + + TypeReference fType = getFieldType(memberNode, fieldAccess); char[] token = fType.getLastToken(); - Expression thisFieldAccessor = createFieldAccessor(field, fieldAccess, source); - Expression otherFieldAccessor = createFieldAccessor(field, fieldAccess, source, otherName); + Expression thisFieldAccessor = isMethod ? createMethodAccessor(memberNode, source) : createFieldAccessor(memberNode, fieldAccess, source); + Expression otherFieldAccessor = isMethod ? createMethodAccessor(memberNode, source, otherName) : createFieldAccessor(memberNode, fieldAccess, source, otherName); if (fType.dimensions() == 0 && token != null) { if (Arrays.equals(TypeConstants.FLOAT, token)) { @@ -700,8 +663,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler onParam) { /* protected boolean canEqual(final java.lang.Object other) { * return other instanceof Outer.Inner.MyType; @@ -788,8 +749,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler { handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString"); ToString ann = annotation.getInstance(); - List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); + List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); if (members == null) return; Boolean callSuper = ann.callSuper(); @@ -105,11 +105,11 @@ public class HandleToString extends EclipseAnnotationHandler { Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); + List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); } - public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List> members, + public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List> members, boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) { TypeDeclaration typeDecl = null; @@ -145,7 +145,7 @@ public class HandleToString extends EclipseAnnotationHandler { } } - public static MethodDeclaration createToString(EclipseNode type, Collection> members, + public static MethodDeclaration createToString(EclipseNode type, Collection> members, boolean includeNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) { String typeName = getTypeName(type); @@ -163,7 +163,7 @@ public class HandleToString extends EclipseAnnotationHandler { } else if (members.isEmpty()) { prefix = (typeName + "()").toCharArray(); } else if (includeNames) { - ToStringMember firstMember = members.iterator().next(); + Included firstMember = members.iterator().next(); String name = firstMember.getInc() == null ? "" : firstMember.getInc().name(); if (name.isEmpty()) name = firstMember.getNode().getName(); prefix = (typeName + "(" + name + "=").toCharArray(); @@ -187,7 +187,7 @@ public class HandleToString extends EclipseAnnotationHandler { first = false; } - for (ToStringMember member : members) { + for (Included member : members) { EclipseNode memberNode = member.getNode(); TypeReference fieldType = getFieldType(memberNode, fieldAccess); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 7cf78392..fb3b45a4 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -54,11 +54,12 @@ import lombok.Builder; import lombok.Builder.ObtainVia; import lombok.ConfigurationKeys; import lombok.Singular; +import lombok.ToString; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.core.handlers.HandlerUtil; -import lombok.core.handlers.InclusionExclusionUtils.ToStringMember; +import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.experimental.NonFinal; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; @@ -399,10 +400,10 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { - java.util.List> fieldNodes = new ArrayList>(); + java.util.List> fieldNodes = new ArrayList>(); for (BuilderFieldData bfd : builderFields) { for (JavacNode f : bfd.createdFields) { - fieldNodes.add(new ToStringMember(f, null, true)); + fieldNodes.add(new Included(f, null, true)); } } diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index b3650ca6..12071cfd 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,6 +35,8 @@ import lombok.core.AST.Kind; import lombok.core.configuration.CallSuperType; import lombok.core.AnnotationValues; import lombok.core.handlers.HandlerUtil; +import lombok.core.handlers.InclusionExclusionUtils; +import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -73,45 +75,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler annotation) { - if (annotation.isExplicit("exclude")) { - for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().exclude()), type, true, true)) { - annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i); - } - } - if (annotation.isExplicit("of")) { - for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().of()), type, false, false)) { - annotation.setWarning("of", "This field does not exist.", i); - } - } - } - @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode"); deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class); EqualsAndHashCode ann = annotation.getInstance(); - List excludes = List.from(ann.exclude()); - List includes = List.from(ann.of()); + java.util.List> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode); JavacNode typeNode = annotationNode.up(); List onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode); - checkForBogusFieldNames(typeNode, annotation); Boolean callSuper = ann.callSuper(); if (!annotation.isExplicit("callSuper")) callSuper = null; - if (!annotation.isExplicit("exclude")) excludes = null; - if (!annotation.isExplicit("of")) includes = null; - - if (excludes != null && includes != null) { - excludes = null; - annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored."); - } Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam); + generateMethods(typeNode, annotationNode, members, callSuper, true, fieldAccess, onParam); } public void generateEqualsAndHashCodeForType(JavacNode typeNode, JavacNode source) { @@ -123,10 +103,12 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil()); + java.util.List> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null); + + generateMethods(typeNode, source, members, null, false, access, List.nil()); } - public void generateMethods(JavacNode typeNode, JavacNode source, List excludes, List includes, + public void generateMethods(JavacNode typeNode, JavacNode source, java.util.List> members, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List onParam) { boolean notAClass = true; @@ -180,29 +162,6 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler nodesForEquality = new ListBuffer(); - if (includes != null) { - for (JavacNode child : typeNode.down()) { - if (child.getKind() != Kind.FIELD) continue; - JCVariableDecl fieldDecl = (JCVariableDecl) child.get(); - if (includes.contains(fieldDecl.name.toString())) nodesForEquality.append(child); - } - } else { - for (JavacNode child : typeNode.down()) { - if (child.getKind() != Kind.FIELD) continue; - JCVariableDecl fieldDecl = (JCVariableDecl) child.get(); - //Skip static fields. - if ((fieldDecl.mods.flags & Flags.STATIC) != 0) continue; - //Skip transient fields. - if ((fieldDecl.mods.flags & Flags.TRANSIENT) != 0) continue; - //Skip excluded fields. - if (excludes != null && excludes.contains(fieldDecl.name.toString())) continue; - //Skip fields that start with $ - if (fieldDecl.name.toString().startsWith("$")) continue; - nodesForEquality.append(child); - } - } - boolean isFinal = (((JCClassDecl) typeNode.get()).mods.flags & Flags.FINAL) != 0; boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject; MemberExistsResult equalsExists = methodExists("equals", typeNode, 1); @@ -231,7 +190,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler fields, boolean callSuper, FieldAccess fieldAccess, JCTree source) { + public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List> members, boolean callSuper, FieldAccess fieldAccess, JCTree source) { JavacTreeMaker maker = typeNode.getTreeMaker(); JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.nil()); @@ -257,7 +216,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), - maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), - List.nil()); + maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), + List.nil()); } else { /* ... 1; */ init = maker.Literal(1); @@ -276,10 +235,12 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler member : members) { + JavacNode memberNode = member.getNode(); + JCExpression fType = getFieldType(memberNode, fieldAccess); + boolean isMethod = memberNode.getKind() == Kind.METHOD; + + JCExpression fieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess); if (fType instanceof JCPrimitiveTypeTree) { switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) { case BOOLEAN: @@ -288,7 +249,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"), @@ -332,7 +293,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")), @@ -410,7 +371,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler fields, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source, List onParam) { + public JCMethodDecl createEquals(JavacNode typeNode, java.util.List> members, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source, List onParam) { JavacTreeMaker maker = typeNode.getTreeMaker(); Name oName = typeNode.toName("o"); @@ -439,7 +400,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler other = (Outer.Inner.MyType) o; */ { - if (!fields.isEmpty() || needsCanEqual) { + if (!members.isEmpty() || needsCanEqual) { final JCExpression selfType1 = createTypeReference(typeNode, true), selfType2 = createTypeReference(typeNode, true); statements.append( @@ -468,12 +429,13 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler member : members) { + JavacNode memberNode = member.getNode(); + boolean isMethod = memberNode.getKind() == Kind.METHOD; + + JCExpression fType = getFieldType(memberNode, fieldAccess); + JCExpression thisFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess); + JCExpression otherFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode, maker.Ident(otherName)) : createFieldAccessor(maker, memberNode, fieldAccess, maker.Ident(otherName)); if (fType instanceof JCPrimitiveTypeTree) { switch (((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind()) { case FLOAT: @@ -504,9 +466,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler params = List.of(maker.VarDef(maker.Modifiers(flags, onParam), otherName, objectType, null)); JCBlock body = maker.Block(0, List.of( - maker.Return(maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode, false))))); + maker.Return(maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode, false))))); return recursiveSetGeneratedBy(maker.MethodDef(mods, canEqualName, returnType, List.nil(), params, List.nil(), body, null), source, typeNode.getContext()); } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 8c580207..28d18357 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -32,7 +32,7 @@ import lombok.ToString; import lombok.core.AnnotationValues; import lombok.core.AST.Kind; import lombok.core.handlers.InclusionExclusionUtils; -import lombok.core.handlers.InclusionExclusionUtils.ToStringMember; +import lombok.core.handlers.InclusionExclusionUtils.Included; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -66,7 +66,7 @@ public class HandleToString extends JavacAnnotationHandler { deleteAnnotationIfNeccessary(annotationNode, ToString.class); ToString ann = annotation.getInstance(); - java.util.List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); + java.util.List> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); if (members == null) return; Boolean callSuper = ann.callSuper(); @@ -98,11 +98,11 @@ public class HandleToString extends JavacAnnotationHandler { Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - java.util.List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); + java.util.List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); } - public void generateToString(JavacNode typeNode, JavacNode source, java.util.List> members, + public void generateToString(JavacNode typeNode, JavacNode source, java.util.List> members, boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) { boolean notAClass = true; @@ -138,7 +138,7 @@ public class HandleToString extends JavacAnnotationHandler { } } - static JCMethodDecl createToString(JavacNode typeNode, Collection> members, + static JCMethodDecl createToString(JavacNode typeNode, Collection> members, boolean includeNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) { JavacTreeMaker maker = typeNode.getTreeMaker(); @@ -158,7 +158,7 @@ public class HandleToString extends JavacAnnotationHandler { } else if (members.isEmpty()) { prefix = typeName + "()"; } else if (includeNames) { - ToStringMember firstMember = members.iterator().next(); + Included firstMember = members.iterator().next(); String name = firstMember.getInc() == null ? "" : firstMember.getInc().name(); if (name.isEmpty()) name = firstMember.getNode().getName(); prefix = typeName + "(" + name + "="; @@ -176,7 +176,7 @@ public class HandleToString extends JavacAnnotationHandler { first = false; } - for (ToStringMember member : members) { + for (Included member : members) { JCExpression expr; JCExpression memberAccessor; diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 917e2e9c..b5bc6e6b 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -948,18 +948,19 @@ public class JavacHandlerUtil { } static JCExpression createMethodAccessor(JavacTreeMaker maker, JavacNode method) { - JCExpression receiver; + return createMethodAccessor(maker, method, null); + } + + static JCExpression createMethodAccessor(JavacTreeMaker maker, JavacNode method, JCExpression receiver) { JCMethodDecl methodDecl = (JCMethodDecl) method.get(); - if ((methodDecl.mods.flags & Flags.STATIC) == 0) { + if (receiver == null && (methodDecl.mods.flags & Flags.STATIC) == 0) { receiver = maker.Ident(method.toName("this")); - } else { + } else if (receiver == null) { JavacNode containerNode = method.up(); if (containerNode != null && containerNode.get() instanceof JCClassDecl) { JCClassDecl container = (JCClassDecl) method.up().get(); receiver = maker.Ident(container.name); - } else { - receiver = null; } } -- cgit