diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index ce31bddf7..efba32a9e 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -307,7 +307,7 @@ private ConfigurationKeys() {} * If {@code true}, require a {@code @ToString.Include} annotation on any fields/no-args methods you want to include in lombok's generated `@ToString` method. Otherwise, every (non-static, non-dollar-named) field is included by default (default = false). */ public static final ConfigurationKey TO_STRING_ONLY_EXPLICITLY_INCLUDED = new ConfigurationKey("lombok.toString.onlyExplicitlyIncluded", "Include only fields/methods explicitly marked with @ToString.Include. Otherwise, include all non-static, non-dollar-named fields (default = false).") {}; - + // ----- Builder ----- /** diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java index 34418a2fe..ee236e61a 100644 --- a/src/core/lombok/ToString.java +++ b/src/core/lombok/ToString.java @@ -87,7 +87,8 @@ * @return If {@code true}, don't include non-static fields automatically (default: {@code false}). */ boolean onlyExplicitlyIncluded() default false; - + boolean skipNull() default false; + /** * If present, do not include this field in the generated {@code toString}. */ diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 96366b9af..f2908b70b 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -571,7 +571,7 @@ private static final char[] prefixWith(char[] prefix, char[] name) { fieldNodes.add(new Included(f, null, true, false)); } } - MethodDeclaration md = HandleToString.createToString(job.builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD); + MethodDeclaration md = HandleToString.createToString(job.builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD, false); if (md != null) injectMethod(job.builderType, md); } diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 096398b85..999970995 100644 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -400,7 +400,7 @@ void setBuilderToAbstract() { } } // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. - MethodDeclaration md = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD); + MethodDeclaration md = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD, false); if (md != null) injectMethod(job.builderType, md); } diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index b22d162f9..4670017e8 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -81,9 +81,10 @@ public void handle(AnnotationValues annotation, Annotation ast, Eclips if (members == null) return; Boolean callSuper = ann.callSuper(); - if (!annotation.isExplicit("callSuper")) callSuper = null; - + Boolean skipNull = ann.skipNull(); + if (!annotation.isExplicit("skipNull")) skipNull = null; + Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; @@ -91,7 +92,7 @@ public void handle(AnnotationValues annotation, Annotation ast, Eclips Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration; - generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess); + generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess, skipNull); } public void generateToStringForType(EclipseNode typeNode, EclipseNode errorNode) { @@ -108,11 +109,11 @@ public void generateToStringForType(EclipseNode typeNode, EclipseNode errorNode) FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, onlyExplicitlyIncluded, null, null); - generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); + generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access, null); } public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List> members, - boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) { + boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, Boolean skipNull) { if (!isClassOrEnum(typeNode)) { errorNode.addError("@ToString is only supported on a class or enum."); @@ -142,7 +143,10 @@ public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List> members, - boolean includeNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) { + boolean includeNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean skipNull) { String typeName = getTypeName(type); boolean isEnum = type.isEnumType(); @@ -223,6 +227,7 @@ public static MethodDeclaration createToString(EclipseNode type, Collection member : members) { + if(skipNull && member.getInc() == null) continue; EclipseNode memberNode = member.getNode(); TypeReference fieldType = getFieldType(memberNode, fieldAccess); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 1e023f20c..6655d85e9 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -512,7 +512,7 @@ static class BuilderFieldData { } } - JCMethodDecl md = HandleToString.createToString(job.builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, job.sourceNode); + JCMethodDecl md = HandleToString.createToString(job.builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, job.sourceNode, false); if (md != null) injectMethod(job.builderType, md); } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 11b3d39d4..d60719b74 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -356,7 +356,7 @@ public void handle(AnnotationValues annotation, JCAnnotation ast, } // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. - JCMethodDecl toStringMethod = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, FieldAccess.ALWAYS_FIELD, annotationNode); + JCMethodDecl toStringMethod = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, FieldAccess.ALWAYS_FIELD, annotationNode, false); if (toStringMethod != null) injectMethod(job.builderType, toStringMethod); // If clean methods are requested, add them now. diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 8eccf0793..dfcef7fa8 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -70,16 +70,17 @@ public class HandleToString extends JavacAnnotationHandler { if (members == null) return; Boolean callSuper = ann.callSuper(); - if (!annotation.isExplicit("callSuper")) callSuper = null; - + Boolean skipNull = ann.skipNull(); + if (!annotation.isExplicit("skipNull")) skipNull = null; + Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; boolean includeFieldNames = annotationNode.getAst().getBooleanAnnotationValue(annotation, "includeFieldNames", ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess); + generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess, skipNull); } public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) { @@ -96,11 +97,11 @@ public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) { FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; java.util.List> members = InclusionExclusionUtils.handleToStringMarking(typeNode, onlyExplicitlyIncluded, null, null); - generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); + generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access, null); } public void generateToString(JavacNode typeNode, JavacNode source, java.util.List> members, - boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) { + boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, Boolean skipNull) { if (!isClassOrEnum(typeNode)) { source.addError("@ToString is only supported on a class or enum."); @@ -130,7 +131,10 @@ public void generateToString(JavacNode typeNode, JavacNode source, java.util.Lis } } } - JCMethodDecl method = createToString(typeNode, members, includeFieldNames, callSuper, fieldAccess, source); + if(skipNull == null) { + skipNull = false; + } + JCMethodDecl method = createToString(typeNode, members, includeFieldNames, callSuper, fieldAccess, source, skipNull); injectMethod(typeNode, method); break; case EXISTS_BY_LOMBOK: @@ -145,7 +149,7 @@ public void generateToString(JavacNode typeNode, JavacNode source, java.util.Lis } static JCMethodDecl createToString(JavacNode typeNode, Collection> members, - boolean includeNames, boolean callSuper, FieldAccess fieldAccess, JavacNode source) { + boolean includeNames, boolean callSuper, FieldAccess fieldAccess, JavacNode source, boolean skipNull) { JavacTreeMaker maker = typeNode.getTreeMaker(); @@ -196,6 +200,7 @@ static JCMethodDecl createToString(JavacNode typeNode, Collection member : members) { + if(skipNull && member.getInc() == null) continue; JCExpression expr; JCExpression memberAccessor;