aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/EqualsAndHashCode.java29
-rw-r--r--src/core/lombok/ToString.java4
-rw-r--r--src/core/lombok/core/AnnotationValues.java57
-rw-r--r--src/core/lombok/core/handlers/InclusionExclusionUtils.java79
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java14
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java7
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java140
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java14
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java7
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java111
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java14
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java11
12 files changed, 265 insertions, 222 deletions
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()}.
+ * <p>
+ * 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.
* <p>
* Mutually exclusive with {@link #exclude()}.
+ * <p>
+ * 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 (<em>default</em>: 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 {
* <p>
* Mutually exclusive with {@link #exclude()}.
* <p>
- * 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 (<em>default</em>: 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<A extends Annotation> {
private A cachedInstance = null;
+ public List<String> 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<String> out = new ArrayList<String>(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> T getDefaultIf(String methodName, Class<T> 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<String> excludes, List<String> 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<L> {
+ public static class Included<L, I extends Annotation> {
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 <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> List<ToStringMember<L>> handleToStringMarking(LombokNode<A, L, N> typeNode, AnnotationValues<ToString> annotation, LombokNode<A, L, N> annotationNode) {
- ToString ann = annotation == null ? null : annotation.getInstance();
- List<String> oldExcludes = (ann != null && annotation.isExplicit("exclude")) ? Arrays.asList(ann.exclude()) : null;
- List<String> oldIncludes = (ann != null && annotation.isExplicit("of")) ? Arrays.asList(ann.of()) : null;
+ private static String innerAnnName(Class<? extends Annotation> type) {
+ String name = type.getSimpleName();
+ Class<?> c = type.getEnclosingClass();
+ while (c != null) {
+ name = c.getSimpleName() + "." + name;
+ c = c.getEnclosingClass();
+ }
+ return name;
+ }
+
+ public static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N, I extends Annotation> List<Included<L, I>> handleIncludeExcludeMarking(Class<I> inclType, String replaceName, Class<? extends Annotation> exclType, LombokNode<A, L, N> typeNode, AnnotationValues<?> annotation, LombokNode<A, L, N> annotationNode) {
+ List<String> oldExcludes = (annotation != null && annotation.isExplicit("exclude")) ? annotation.getAsStringList("exclude") : null;
+ List<String> 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<ToStringMember<L>> members = new ArrayList<ToStringMember<L>>();
+ List<Included<L, I>> members = new ArrayList<Included<L, I>>();
List<String> namesToAutoExclude = new ArrayList<String>();
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<ToString.Include> 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<I> 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<L>(child, inc, false));
+ members.add(new Included<L, I>(child, inc, false));
continue;
}
if (onlyExplicitlyIncluded) continue;
if (oldIncludes != null) {
- if (child.getKind() == Kind.FIELD && oldIncludes.contains(name)) members.add(new ToStringMember<L>(child, null, false));
+ if (child.getKind() == Kind.FIELD && oldIncludes.contains(name)) members.add(new Included<L, I>(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<L>(child, null, true));
+ members.add(new Included<L, I>(child, null, true));
}
/* delete default-included fields with the same name as an explicit inclusion */ {
- Iterator<ToStringMember<L>> it = members.iterator();
+ Iterator<Included<L, I>> it = members.iterator();
while (it.hasNext()) {
- ToStringMember<L> m = it.next();
+ Included<L, I> 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<ToStringMember<L>>() {
- @Override public int compare(ToStringMember<L> a, ToStringMember<L> b) {
+ return members;
+ }
+
+ public static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> List<Included<L, ToString.Include>> handleToStringMarking(LombokNode<A, L, N> typeNode, AnnotationValues<ToString> annotation, LombokNode<A, L, N> annotationNode) {
+ List<Included<L, ToString.Include>> members = handleIncludeExcludeMarking(ToString.Include.class, "name", ToString.Exclude.class, typeNode, annotation, annotationNode);
+
+ Collections.sort(members, new Comparator<Included<L, ToString.Include>>() {
+ @Override public int compare(Included<L, ToString.Include> a, Included<L, ToString.Include> 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 <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> List<Included<L, EqualsAndHashCode.Include>> handleEqualsAndHashCodeMarking(LombokNode<A, L, N> typeNode, AnnotationValues<EqualsAndHashCode> annotation, LombokNode<A, L, N> 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<Builder> {
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
- List<ToStringMember<EclipseNode>> fieldNodes = new ArrayList<ToStringMember<EclipseNode>>();
+ List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>();
for (BuilderFieldData bfd : builderFields) {
for (EclipseNode f : bfd.createdFields) {
- fieldNodes.add(new ToStringMember<EclipseNode>(f, null, true));
+ fieldNodes.add(new Included<EclipseNode, ToString.Include>(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<EqualsAndH
public static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"byte", "short", "int", "long", "char", "boolean", "double", "float")));
- public void checkForBogusFieldNames(EclipseNode type, AnnotationValues<EqualsAndHashCode> 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<Annotation>());
- }
-
@Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
EqualsAndHashCode ann = annotation.getInstance();
- List<String> excludes = Arrays.asList(ann.exclude());
- List<String> includes = Arrays.asList(ann.of());
- EclipseNode typeNode = annotationNode.up();
+ List<Included<EclipseNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
List<Annotation> 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<Included<EclipseNode, EqualsAndHashCode.Include>> 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<Annotation>());
}
- public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
- assert excludes == null || includes == null;
+ public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, EqualsAndHashCode.Include>> members,
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
TypeDeclaration typeDecl = null;
@@ -171,7 +151,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
if (callSuper == null) {
try {
- callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ callSuper = ((Boolean) EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {
throw new InternalError("Lombok bug - this cannot happen - can't find callSuper field in EqualsAndHashCode annotation.");
}
@@ -208,27 +188,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
- List<EclipseNode> nodesForEquality = new ArrayList<EclipseNode>();
- 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<EqualsAndH
//fallthrough
}
- MethodDeclaration equalsMethod = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
+ MethodDeclaration equalsMethod = createEquals(typeNode, members, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
equalsMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope);
injectMethod(typeNode, equalsMethod);
@@ -267,17 +226,16 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
injectMethod(typeNode, canEqualMethod);
}
- MethodDeclaration hashCodeMethod = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
+ MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, errorNode.get(), fieldAccess);
hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope);
injectMethod(typeNode, hashCodeMethod);
}
- public MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+ public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> 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<EqualsAndH
List<Statement> statements = new ArrayList<Statement>();
- 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<EqualsAndH
}
}
- /*int result = ... */{
+ /* int result = ... */ {
LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE);
setGeneratedBy(resultDecl, source);
final Expression init;
@@ -334,11 +292,14 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
statements.add(resultDecl);
}
- for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, fieldAccess);
- char[] dollarFieldName = ("$" + field.getName()).toCharArray();
+ for (Included<EclipseNode, EqualsAndHashCode.Include> 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<EqualsAndH
return arr == null ? 0 : arr.length;
}
- public MethodDeclaration createEquals(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> onParam) {
+ public MethodDeclaration createEquals(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> 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<EqualsAndH
char[] otherName = "other".toCharArray();
/* Outer.Inner.MyType<?> 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<EqualsAndH
statements.add(ifSuperEquals);
}
- for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, fieldAccess);
+ for (Included<EclipseNode, EqualsAndHashCode.Include> 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<EqualsAndH
/* final java.lang.Object this$fieldName = this.fieldName; */
/* final java.lang.Object other$fieldName = other.fieldName; */
/* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
- char[] thisDollarFieldName = ("this$" + field.getName()).toCharArray();
- char[] otherDollarFieldName = ("other$" + field.getName()).toCharArray();
+ char[] thisDollarFieldName = ("this" + (isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
+ char[] otherDollarFieldName = ("other" + (isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
statements.add(createLocalDeclaration(source, thisDollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), thisFieldAccessor));
statements.add(createLocalDeclaration(source, otherDollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), otherFieldAccessor));
@@ -714,7 +677,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
setGeneratedBy(other1, source);
SingleNameReference other2 = new SingleNameReference(otherDollarFieldName, p);
setGeneratedBy(other2, source);
-
NullLiteral nullLiteral = new NullLiteral(pS, pE);
setGeneratedBy(nullLiteral, source);
@@ -777,7 +739,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
return method;
}
-
public MethodDeclaration createCanEqual(EclipseNode type, ASTNode source, List<Annotation> onParam) {
/* protected boolean canEqual(final java.lang.Object other) {
* return other instanceof Outer.Inner.MyType;
@@ -788,8 +749,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
char[] otherName = "other".toCharArray();
- 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.PROTECTED);
method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index 14a2374b..02a19f8d 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.java
@@ -37,7 +37,7 @@ import lombok.ToString;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.handlers.InclusionExclusionUtils;
-import lombok.core.handlers.InclusionExclusionUtils.ToStringMember;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
@@ -73,7 +73,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
ToString ann = annotation.getInstance();
- List<ToStringMember<EclipseNode>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode);
+ List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode);
if (members == null) return;
Boolean callSuper = ann.callSuper();
@@ -105,11 +105,11 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
- L