aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@tipit.to>2009-09-03 01:44:59 +0200
committerReinier Zwitserloot <reinier@tipit.to>2009-09-03 01:44:59 +0200
commitf1124aad02569c983cb8979445245141bf029a88 (patch)
tree80d25bb1dddcfce46931f298b6f70ebdbd2e3e13 /src
parent6b7919166e9a550d7d2b1f7156c794e76905fcab (diff)
downloadlombok-f1124aad02569c983cb8979445245141bf029a88.tar.gz
lombok-f1124aad02569c983cb8979445245141bf029a88.tar.bz2
lombok-f1124aad02569c983cb8979445245141bf029a88.zip
Addressed issue #32: The @EqualsAndHashCode and @ToString annotations now support explicitly listing the fields to use, via the new 'of' parameter.
We've also added any fields that start with $ to the default excludes list. Lombok itself can generate these fields ($lock of @Synchronized, for example), and in general they probably should count as effectively not part of the class.
Diffstat (limited to 'src')
-rw-r--r--src/lombok/EqualsAndHashCode.java14
-rw-r--r--src/lombok/ToString.java14
-rw-r--r--src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java91
-rw-r--r--src/lombok/eclipse/handlers/HandleToString.java77
-rw-r--r--src/lombok/eclipse/handlers/PKG.java25
-rw-r--r--src/lombok/javac/handlers/HandleEqualsAndHashCode.java83
-rw-r--r--src/lombok/javac/handlers/HandleToString.java77
-rw-r--r--src/lombok/javac/handlers/PKG.java26
8 files changed, 266 insertions, 141 deletions
diff --git a/src/lombok/EqualsAndHashCode.java b/src/lombok/EqualsAndHashCode.java
index ac2b95a1..597a6933 100644
--- a/src/lombok/EqualsAndHashCode.java
+++ b/src/lombok/EqualsAndHashCode.java
@@ -35,8 +35,9 @@ import java.lang.annotation.Target;
* a method; any method named <code>hashCode</code> will make <code>&#64;EqualsAndHashCode</code> not generate that method,
* for example.
* <p>
- * All fields that are non-static and non-transient are used in the equality check and hashCode generation. You can exclude
- * more fields by specifying them in the <code>exclude</code> parameter.
+ * By default, all fields that are non-static and non-transient are used in the equality check and hashCode generation.
+ * You can exclude more fields by specifying them in the <code>exclude</code> parameter. You can also explicitly specify
+ * the fields that are to be used by specifying them in the <code>of</code> parameter.
* <p>
* Normally, auto-generating <code>hashCode</code> and <code>equals</code> implementations in a subclass is a bad idea, as
* the superclass also defines fields, for which equality checks/hashcodes won't be auto-generated. Therefore, a warning
@@ -58,10 +59,19 @@ public @interface EqualsAndHashCode {
/**
* Any fields listed here will not be taken into account in the generated
* <code>equals</code> and <code>hashCode</code> implementations.
+ * Mutually exclusive with {@link #of()}.
*/
String[] exclude() default {};
/**
+ * If present, explicitly lists the fields that are to be printed.
+ * Normally, all non-static, non-transient fields are printed.
+ * <p>
+ * Mutually exclusive with {@link #exclude()}.
+ */
+ String[] of() default {};
+
+ /**
* Call on the superclass's implementations of <code>equals</code> and <code>hashCode</code> before calculating
* for the fields in this class.
* <strong>default: false</strong>
diff --git a/src/lombok/ToString.java b/src/lombok/ToString.java
index 10c61807..46d9dabe 100644
--- a/src/lombok/ToString.java
+++ b/src/lombok/ToString.java
@@ -33,8 +33,9 @@ import java.lang.annotation.Target;
* that it's doing nothing at all. The parameter list and return type are not relevant when deciding to skip generation of
* the method; any method named <code>toString</code> will make <code>&#64;ToString</code> not generate anything.
* <p>
- * All fields that are non-static are used in the toString generation. You can exclude fields by specifying them
- * in the <code>exclude</code> parameter.
+ * By default, all fields that are non-static are used in the toString generation. You can exclude fields by specifying them
+ * in the <code>exclude</code> parameter. You can also explicitly specify the fields that
+ * are to be used by specifying them in the <code>of</code> parameter.
* <p>
* Array fields are handled by way of {@link java.util.Arrays#deepToString(Object[])} where necessary.
* The downside is that arrays with circular references (arrays that contain themselves,
@@ -60,10 +61,19 @@ public @interface ToString {
/**
* Any fields listed here will not be printed in the generated <code>toString</code> implementation.
+ * Mutually exclusive with {@link #of()}.
*/
String[] exclude() default {};
/**
+ * If present, explicitly lists the fields that are to be printed.
+ * Normally, all non-static fields are printed.
+ * <p>
+ * Mutually exclusive with {@link #exclude()}.
+ */
+ String[] of() default {};
+
+ /**
* Include the result of the superclass's implementation of <code>toString</code> in the output.
* <strong>default: false</strong>
*/
diff --git a/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 5896dc2d..46792236 100644
--- a/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -91,24 +91,17 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
private static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"byte", "short", "int", "long", "char", "boolean", "double", "float")));
- private void checkForBogusExcludes(Node type, AnnotationValues<EqualsAndHashCode> annotation) {
- List<String> list = Arrays.asList(annotation.getInstance().exclude());
- boolean[] matched = new boolean[list.size()];
-
- for ( Node child : type.down() ) {
- if ( list.isEmpty() ) break;
- if ( child.getKind() != Kind.FIELD ) continue;
- if ( (((FieldDeclaration)child.get()).modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
- if ( (((FieldDeclaration)child.get()).modifiers & ClassFileConstants.AccTransient) != 0 ) continue;
- int idx = list.indexOf(child.getName());
- if ( idx > -1 ) matched[idx] = true;
- }
-
- for ( int i = 0 ; i < list.size() ; i++ ) {
- if ( !matched[i] ) {
+ private void checkForBogusFieldNames(Node 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(Node typeNode, Node errorNode) {
@@ -121,26 +114,34 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
}
}
- boolean callSuper = false;
- try {
- callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
- } catch ( Exception ignore ) {}
- generateMethods(typeNode, errorNode, Collections.<String>emptyList(), callSuper, true, false);
+ generateMethods(typeNode, errorNode, null, null, null, false);
}
@Override public boolean handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, Node annotationNode) {
EqualsAndHashCode ann = annotation.getInstance();
List<String> excludes = Arrays.asList(ann.exclude());
+ List<String> includes = Arrays.asList(ann.of());
Node typeNode = annotationNode.up();
- checkForBogusExcludes(typeNode, annotation);
+ 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.");
+ }
- return generateMethods(typeNode, annotationNode, excludes,
- ann.callSuper(), annotation.getRawExpression("callSuper") == null, true);
+ return generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true);
}
- public boolean generateMethods(Node typeNode, Node errorNode, List<String> excludes,
- boolean callSuper, boolean implicit, boolean whineIfExists) {
+ public boolean generateMethods(Node typeNode, Node errorNode, List<String> excludes, List<String> includes,
+ Boolean callSuper, boolean whineIfExists) {
+ assert excludes == null || includes == null;
+
TypeDeclaration typeDecl = null;
if ( typeNode.get() instanceof TypeDeclaration ) typeDecl = (TypeDeclaration) typeNode.get();
@@ -153,6 +154,14 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
return false;
}
+ boolean implicitCallSuper = callSuper == null;
+
+ if ( callSuper == null ) {
+ try {
+ callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ } catch ( Exception ignore ) {}
+ }
+
boolean isDirectDescendantOfObject = true;
if ( typeDecl.superclass != null ) {
@@ -165,21 +174,31 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
return true;
}
- if ( !isDirectDescendantOfObject && !callSuper && implicit ) {
+ if ( !isDirectDescendantOfObject && !callSuper && implicitCallSuper ) {
errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.");
}
List<Node> nodesForEquality = new ArrayList<Node>();
- for ( Node child : typeNode.down() ) {
- if ( child.getKind() != Kind.FIELD ) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- //Skip static fields.
- if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
- //Skip transient fields.
- if ( (fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0 ) continue;
- //Skip excluded fields.
- if ( excludes.contains(new String(fieldDecl.name)) ) continue;
- nodesForEquality.add(child);
+ if ( includes != null ) {
+ for ( Node 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 ( Node child : typeNode.down() ) {
+ if ( child.getKind() != Kind.FIELD ) continue;
+ FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
+ //Skip static fields.
+ if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
+ //Skip transient fields.
+ if ( (fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0 ) continue;
+ //Skip excluded fields.
+ if ( excludes != null && excludes.contains(new String(fieldDecl.name)) ) continue;
+ //Skip fields that start with $.
+ if ( fieldDecl.name.length > 0 && fieldDecl.name[0] == '$' ) continue;
+ nodesForEquality.add(child);
+ }
}
switch ( methodExists("hashCode", typeNode) ) {
diff --git a/src/lombok/eclipse/handlers/HandleToString.java b/src/lombok/eclipse/handlers/HandleToString.java
index f2c4e9cf..d639e3fd 100644
--- a/src/lombok/eclipse/handlers/HandleToString.java
+++ b/src/lombok/eclipse/handlers/HandleToString.java
@@ -66,25 +66,20 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleToString implements EclipseAnnotationHandler<ToString> {
- private void checkForBogusExcludes(Node type, AnnotationValues<ToString> annotation) {
- List<String> list = Arrays.asList(annotation.getInstance().exclude());
- boolean[] matched = new boolean[list.size()];
-
- for ( Node child : type.down() ) {
- if ( list.isEmpty() ) break;
- if ( child.getKind() != Kind.FIELD ) continue;
- if ( (((FieldDeclaration)child.get()).modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
- int idx = list.indexOf(child.getName());
- if ( idx > -1 ) matched[idx] = true;
- }
-
- for ( int i = 0 ; i < list.size() ; i++ ) {
- if ( !matched[i] ) {
+ private void checkForBogusFieldNames(Node type, AnnotationValues<ToString> annotation) {
+ if ( annotation.isExplicit("exclude") ) {
+ for ( int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().exclude()), type, true, false) ) {
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 generateToStringForType(Node typeNode, Node errorNode) {
for ( Node child : typeNode.down() ) {
if ( child.getKind() == Kind.ANNOTATION ) {
@@ -95,29 +90,31 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
}
}
- boolean includeFieldNames = false;
- boolean callSuper = false;
+ boolean includeFieldNames = true;
try {
includeFieldNames = ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue();
} catch ( Exception ignore ) {}
- try {
- callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
- } catch ( Exception ignore ) {}
- generateToString(typeNode, errorNode, Collections.<String>emptyList(), includeFieldNames, callSuper, false);
+ generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false);
}
public boolean handle(AnnotationValues<ToString> annotation, Annotation ast, Node annotationNode) {
ToString ann = annotation.getInstance();
List<String> excludes = Arrays.asList(ann.exclude());
+ List<String> includes = Arrays.asList(ann.of());
Node typeNode = annotationNode.up();
+ Boolean callSuper = ann.callSuper();
- checkForBogusExcludes(typeNode, annotation);
+ if ( !annotation.isExplicit("callSuper") ) callSuper = null;
+ if ( !annotation.isExplicit("exclude") ) excludes = null;
+ if ( !annotation.isExplicit("of") ) includes = null;
- return generateToString(typeNode, annotationNode, excludes, ann.includeFieldNames(), ann.callSuper(), true);
+ checkForBogusFieldNames(typeNode, annotation);
+
+ return generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true);
}
- public boolean generateToString(Node typeNode, Node errorNode, List<String> excludes,
- boolean includeFieldNames, boolean callSuper, boolean whineIfExists) {
+ public boolean generateToString(Node typeNode, Node errorNode, List<String> excludes, List<String> includes,
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists) {
TypeDeclaration typeDecl = null;
if ( typeNode.get() instanceof TypeDeclaration ) typeDecl = (TypeDeclaration) typeNode.get();
@@ -130,15 +127,31 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
return false;
}
+ if ( callSuper == null ) {
+ try {
+ callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ } catch ( Exception ignore ) {}
+ }
+
List<Node> nodesForToString = new ArrayList<Node>();
- for ( Node child : typeNode.down() ) {
- if ( child.getKind() != Kind.FIELD ) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- //Skip static fields.
- if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
- //Skip excluded fields.
- if ( excludes.contains(new String(fieldDecl.name)) ) continue;
- nodesForToString.add(child);
+ if ( includes != null ) {
+ for ( Node child : typeNode.down() ) {
+ if ( child.getKind() != Kind.FIELD ) continue;
+ FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
+ if ( includes.contains(new String(fieldDecl.name)) ) nodesForToString.add(child);
+ }
+ } else {
+ for ( Node child : typeNode.down() ) {
+ if ( child.getKind() != Kind.FIELD ) continue;
+ FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
+ //Skip static fields.
+ if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
+ //Skip excluded fields.
+ if ( excludes.contains(new String(fieldDecl.name)) ) continue;
+ //Skip fields that start with $
+ if ( fieldDecl.name.length > 0 && fieldDecl.name[0] == '$' ) continue;
+ nodesForToString.add(child);
+ }
}
switch ( methodExists("toString", typeNode) ) {
diff --git a/src/lombok/eclipse/handlers/PKG.java b/src/lombok/eclipse/handlers/PKG.java
index 98c60524..58fac4a1 100644
--- a/src/lombok/eclipse/handlers/PKG.java
+++ b/src/lombok/eclipse/handlers/PKG.java
@@ -33,6 +33,7 @@ import lombok.core.TransformationsUtil;
import lombok.core.AST.Kind;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAST;
+import lombok.eclipse.EclipseAST.Node;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -54,6 +55,7 @@ import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
class PKG {
private PKG() {}
@@ -273,4 +275,27 @@ class PKG {
ann.bits |= ASTNode.HasBeenGenerated;
return ann;
}
+
+ static List<Integer> createListOfNonExistentFields(List<String> list, Node type, boolean excludeStandard, boolean excludeTransient) {
+ boolean[] matched = new boolean[list.size()];
+
+ for ( Node child : type.down() ) {
+ if ( list.isEmpty() ) break;
+ if ( child.getKind() != Kind.FIELD ) continue;
+ if ( excludeStandard ) {
+ if ( (((FieldDeclaration)child.get()).modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
+ if ( child.getName().startsWith("$") ) continue;
+ }
+ if ( excludeTransient && (((FieldDeclaration)child.get()).modifiers & ClassFileConstants.AccTransient) != 0 ) continue;
+ int idx = list.indexOf(child.getName());
+ if ( idx > -1 ) matched[idx] = true;
+ }
+
+ List<Integer> problematic = new ArrayList<Integer>();
+ for ( int i = 0 ; i < list.size() ; i++ ) {
+ if ( !matched[i] ) problematic.add(i);
+ }
+
+ return problematic;
+ }
}
diff --git a/src/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 8c3124c9..16a04da7 100644
--- a/src/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -22,7 +22,6 @@
package lombok.javac.handlers;
import static lombok.javac.handlers.PKG.*;
-
import lombok.EqualsAndHashCode;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
@@ -59,35 +58,33 @@ import com.sun.tools.javac.util.Name;
*/
@ProviderFor(JavacAnnotationHandler.class)
public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAndHashCode> {
- private void checkForBogusExcludes(Node type, AnnotationValues<EqualsAndHashCode> annotation) {
- List<String> list = List.from(annotation.getInstance().exclude());
- boolean[] matched = new boolean[list.size()];
-
- for ( Node child : type.down() ) {
- if ( list.isEmpty() ) break;
- if ( child.getKind() != Kind.FIELD ) continue;
- if ( (((JCVariableDecl)child.get()).mods.flags & Flags.STATIC) != 0 ) continue;
- if ( (((JCVariableDecl)child.get()).mods.flags & Flags.TRANSIENT) != 0 ) continue;
- int idx = list.indexOf(child.getName());
- if ( idx > -1 ) matched[idx] = true;
- }
-
- for ( int i = 0 ; i < list.size() ; i++ ) {
- if ( !matched[i] ) {
+ private void checkForBogusFieldNames(Node type, AnnotationValues<EqualsAndHashCode> 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 boolean handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, Node annotationNode) {
EqualsAndHashCode ann = annotation.getInstance();
List<String> excludes = List.from(ann.exclude());
+ List<String> includes = List.from(ann.of());
Node typeNode = annotationNode.up();
- checkForBogusExcludes(typeNode, annotation);
+ checkForBogusFieldNames(typeNode, annotation);
- return generateMethods(typeNode, annotationNode, excludes,
- ann.callSuper(), annotation.getRawExpression("callSuper") == null, true);
+ Boolean callSuper = ann.callSuper();
+ if ( !annotation.isExplicit("callSuper") ) callSuper = null;
+ if ( !annotation.isExplicit("exclude") ) excludes = null;
+ if ( !annotation.isExplicit("of") ) includes = null;
+
+ return generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true);
}
public void generateEqualsAndHashCodeForType(Node typeNode, Node errorNode) {
@@ -100,15 +97,11 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
}
}
- boolean callSuper = false;
- try {
- callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
- } catch ( Exception ignore ) {}
- generateMethods(typeNode, errorNode, List.<String>nil(), callSuper, true, false);
+ generateMethods(typeNode, errorNode, null, null, null, false);
}
- private boolean generateMethods(Node typeNode, Node errorNode, List<String> excludes,
- boolean callSuper, boolean implicit, boolean whineIfExists) {
+ private boolean generateMethods(Node typeNode, Node errorNode, List<String> excludes, List<String> includes,
+ Boolean callSuper, boolean whineIfExists) {
boolean notAClass = true;
if ( typeNode.get() instanceof JCClassDecl ) {
long flags = ((JCClassDecl)typeNode.get()).mods.flags;
@@ -121,6 +114,12 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
}
boolean isDirectDescendantOfObject = true;
+ boolean implicitCallSuper = callSuper == null;
+ if ( callSuper == null ) {
+ try {
+ callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ } catch ( Exception ignore ) {}
+ }
JCTree extending = ((JCClassDecl)typeNode.get()).extending;
if ( extending != null ) {
@@ -133,21 +132,31 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
return true;
}
- if ( !isDirectDescendantOfObject && !callSuper && implicit ) {
+ if ( !isDirectDescendantOfObject && !callSuper && implicitCallSuper ) {
errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.");
}
List<Node> nodesForEquality = List.nil();
- for ( Node 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.contains(fieldDecl.name.toString()) ) continue;
- nodesForEquality = nodesForEquality.append(child);
+ if ( includes != null ) {
+ for ( Node child : typeNode.down() ) {
+ if ( child.getKind() != Kind.FIELD ) continue;
+ JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
+ if ( includes.contains(fieldDecl.name.toString()) ) nodesForEquality = nodesForEquality.append(child);
+ }
+ } else {
+ for ( Node 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 = nodesForEquality.append(child);
+ }
}
switch ( methodExists("hashCode", typeNode) ) {
diff --git a/src/lombok/javac/handlers/HandleToString.java b/src/lombok/javac/handlers/HandleToString.java
index 49f0d15c..7cdbb6e1 100644
--- a/src/lombok/javac/handlers/HandleToString.java
+++ b/src/lombok/javac/handlers/HandleToString.java
@@ -54,33 +54,34 @@ import com.sun.tools.javac.util.List;
*/
@ProviderFor(JavacAnnotationHandler.class)
public class HandleToString implements JavacAnnotationHandler<ToString> {
- private void checkForBogusExcludes(Node type, AnnotationValues<ToString> annotation) {
- List<String> list = List.from(annotation.getInstance().exclude());
- boolean[] matched = new boolean[list.size()];
-
- for ( Node child : type.down() ) {
- if ( list.isEmpty() ) break;
- if ( child.getKind() != Kind.FIELD ) continue;
- if ( (((JCVariableDecl)child.get()).mods.flags & Flags.STATIC) != 0 ) continue;
- int idx = list.indexOf(child.getName());
- if ( idx > -1 ) matched[idx] = true;
- }
-
- for ( int i = 0 ; i < list.size() ; i++ ) {
- if ( !matched[i] ) {
+ private void checkForBogusFieldNames(Node type, AnnotationValues<ToString> annotation) {
+ if ( annotation.isExplicit("exclude") ) {
+ for ( int i : createListOfNonExistentFields(List.from(annotation.getInstance().exclude()), type, true, false) ) {
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 boolean handle(AnnotationValues<ToString> annotation, JCAnnotation ast, Node annotationNode) {
ToString ann = annotation.getInstance();
List<String> excludes = List.from(ann.exclude());
+ List<String> includes = List.from(ann.of());
Node typeNode = annotationNode.up();
- checkForBogusExcludes(typeNode, annotation);
+ checkForBogusFieldNames(typeNode, annotation);
- return generateToString(typeNode, annotationNode, excludes, ann.includeFieldNames(), ann.callSuper(), true);
+ Boolean callSuper = ann.callSuper();
+
+ if ( !annotation.isExplicit("callSuper") ) callSuper = null;
+ if ( !annotation.isExplicit("exclude") ) excludes = null;
+ if ( !annotation.isExplicit("of") ) includes = null;
+
+ return generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true);
}
public void generateToStringForType(Node typeNode, Node errorNode) {
@@ -93,39 +94,51 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
}
}
- boolean includeFieldNames = false;
- boolean callSuper = false;
+ boolean includeFieldNames = true;
try {
includeFieldNames = ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue();
} catch ( Exception ignore ) {}
- try {
- callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
- } catch ( Exception ignore ) {}
- generateToString(typeNode, errorNode, List.<String>nil(), includeFieldNames, callSuper, false);
+ generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false);
}
- private boolean generateToString(Node typeNode, Node errorNode, List<String> excludes,
- boolean includeFieldNames, boolean callSuper, boolean whineIfExists) {
+ private boolean generateToString(Node typeNode, Node errorNode, List<String> excludes, List<String> includes,
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists) {
boolean notAClass = true;
if ( typeNode.get() instanceof JCClassDecl ) {
long flags = ((JCClassDecl)typeNode.get()).mods.flags;
notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
}
+ if ( callSuper == null ) {
+ try {
+ callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ } catch ( Exception ignore ) {}
+ }
+
if ( notAClass ) {
errorNode.addError("@ToString is only supported on a class.");
return false;
}
List<Node> nodesForToString = List.nil();
- for ( Node 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 excluded fields.
- if ( excludes.contains(fieldDecl.name.toString()) ) continue;
- nodesForToString = nodesForToString.append(child);
+ if ( includes != null ) {
+ for ( Node child : typeNode.down() ) {
+ if ( child.getKind() != Kind.FIELD ) continue;
+ JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
+ if ( includes.contains(fieldDecl.name.toString()) ) nodesForToString = nodesForToString.append(child);
+ }
+ } else {
+ for ( Node 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 excluded fields.
+ if ( excludes != null && excludes.contains(fieldDecl.name.toString()) ) continue;
+ //Skip fields that start with $.
+ if ( fieldDecl.name.toString().startsWith("$") ) continue;
+ nodesForToString = nodesForToString.append(child);
+ }
}
switch ( methodExists("toString", typeNode) ) {
diff --git a/src/lombok/javac/handlers/PKG.java b/src/lombok/javac/handlers/PKG.java
index 4b05b9ae..0563f33c 100644
--- a/src/lombok/javac/handlers/PKG.java
+++ b/src/lombok/javac/handlers/PKG.java
@@ -288,4 +288,30 @@ class PKG {
JCStatement throwStatement = treeMaker.Throw(exception);
return treeMaker.If(treeMaker.Binary(JCTree.EQ, treeMaker.Ident(fieldName), treeMaker.Literal(TypeTags.BOT, null)), throwStatement, null);
}
+
+ static List<Integer> createListOfNonExistentFields(List<String> list, Node type, boolean excludeStandard, boolean excludeTransient) {
+ boolean[] matched = new boolean[list.size()];
+
+ for ( Node child : type.down() ) {
+ if ( list.isEmpty() ) break;
+ if ( child.getKind() != Kind.FIELD ) continue;
+ JCVariableDecl field = (JCVariableDecl)child.get();
+ if ( excludeStandard ) {
+ if ( (field.mods.flags & Flags.STATIC) != 0 ) continue;
+ if ( field.name.toString().startsWith("$") ) continue;
+ }
+ if ( excludeTransient && (field.mods.flags & Flags.TRANSIENT) != 0 ) continue;
+
+ int idx = list.indexOf(child.getName());
+ if ( idx > -1 ) matched[idx] = true;
+ }
+
+ List<Integer> problematic = List.nil();
+ for ( int i = 0 ; i < list.size() ; i++ ) {
+ if ( !matched[i] ) problematic = problematic.append(i);
+ }
+
+ return problematic;
+ }
+
}