From f1124aad02569c983cb8979445245141bf029a88 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Thu, 3 Sep 2009 01:44:59 +0200 Subject: 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. --- src/lombok/EqualsAndHashCode.java | 14 +++- src/lombok/ToString.java | 14 +++- .../eclipse/handlers/HandleEqualsAndHashCode.java | 91 +++++++++++++--------- src/lombok/eclipse/handlers/HandleToString.java | 77 ++++++++++-------- src/lombok/eclipse/handlers/PKG.java | 25 ++++++ .../javac/handlers/HandleEqualsAndHashCode.java | 83 +++++++++++--------- src/lombok/javac/handlers/HandleToString.java | 77 ++++++++++-------- src/lombok/javac/handlers/PKG.java | 26 +++++++ 8 files changed, 266 insertions(+), 141 deletions(-) (limited to 'src') 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 hashCode will make @EqualsAndHashCode not generate that method, * for example. *

- * 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 exclude 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 exclude parameter. You can also explicitly specify + * the fields that are to be used by specifying them in the of parameter. *

* Normally, auto-generating hashCode and equals 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,9 +59,18 @@ public @interface EqualsAndHashCode { /** * Any fields listed here will not be taken into account in the generated * equals and hashCode 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. + *

+ * Mutually exclusive with {@link #exclude()}. + */ + String[] of() default {}; + /** * Call on the superclass's implementations of equals and hashCode before calculating * for the fields in this class. 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 toString will make @ToString not generate anything. *

- * All fields that are non-static are used in the toString generation. You can exclude fields by specifying them - * in the exclude parameter. + * By default, all fields that are non-static are used in the toString generation. You can exclude fields by specifying them + * in the exclude parameter. You can also explicitly specify the fields that + * are to be used by specifying them in the of parameter. *

* 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,9 +61,18 @@ public @interface ToString { /** * Any fields listed here will not be printed in the generated toString 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. + *

+ * Mutually exclusive with {@link #exclude()}. + */ + String[] of() default {}; + /** * Include the result of the superclass's implementation of toString in the output. * default: false 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 BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet(Arrays.asList( "byte", "short", "int", "long", "char", "boolean", "double", "float"))); - private void checkForBogusExcludes(Node type, AnnotationValues annotation) { - List 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 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 EclipseAnnotationHandleremptyList(), callSuper, true, false); + generateMethods(typeNode, errorNode, null, null, null, false); } @Override public boolean handle(AnnotationValues annotation, Annotation ast, Node annotationNode) { EqualsAndHashCode ann = annotation.getInstance(); List excludes = Arrays.asList(ann.exclude()); + List 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 excludes, - boolean callSuper, boolean implicit, boolean whineIfExists) { + public boolean generateMethods(Node typeNode, Node errorNode, List excludes, List 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 nodesForEquality = new ArrayList(); - 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 { - private void checkForBogusExcludes(Node type, AnnotationValues annotation) { - List 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 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 { } } - 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.emptyList(), includeFieldNames, callSuper, false); + generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false); } public boolean handle(AnnotationValues annotation, Annotation ast, Node annotationNode) { ToString ann = annotation.getInstance(); List excludes = Arrays.asList(ann.exclude()); + List 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 excludes, - boolean includeFieldNames, boolean callSuper, boolean whineIfExists) { + public boolean generateToString(Node typeNode, Node errorNode, List excludes, List 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 { return false; } + if ( callSuper == null ) { + try { + callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue(); + } catch ( Exception ignore ) {} + } + List nodesForToString = new ArrayList(); - 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 createListOfNonExistentFields(List 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 problematic = new ArrayList(); + 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 { - private void checkForBogusExcludes(Node type, AnnotationValues annotation) { - List 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 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 annotation, JCAnnotation ast, Node annotationNode) { EqualsAndHashCode ann = annotation.getInstance(); List excludes = List.from(ann.exclude()); + List 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 JavacAnnotationHandlernil(), callSuper, true, false); + generateMethods(typeNode, errorNode, null, null, null, false); } - private boolean generateMethods(Node typeNode, Node errorNode, List excludes, - boolean callSuper, boolean implicit, boolean whineIfExists) { + private boolean generateMethods(Node typeNode, Node errorNode, List excludes, List 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 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 { - private void checkForBogusExcludes(Node type, AnnotationValues annotation) { - List 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 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 annotation, JCAnnotation ast, Node annotationNode) { ToString ann = annotation.getInstance(); List excludes = List.from(ann.exclude()); + List 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 { } } - 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.nil(), includeFieldNames, callSuper, false); + generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false); } - private boolean generateToString(Node typeNode, Node errorNode, List excludes, - boolean includeFieldNames, boolean callSuper, boolean whineIfExists) { + private boolean generateToString(Node typeNode, Node errorNode, List excludes, List 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 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 createListOfNonExistentFields(List 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 problematic = List.nil(); + for ( int i = 0 ; i < list.size() ; i++ ) { + if ( !matched[i] ) problematic = problematic.append(i); + } + + return problematic; + } + } -- cgit