diff options
9 files changed, 71 insertions, 27 deletions
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 05550a06..457246e7 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -291,6 +291,13 @@ public class ConfigurationKeys { */ public static final ConfigurationKey<Boolean> TO_STRING_INCLUDE_FIELD_NAMES = new ConfigurationKey<Boolean>("lombok.toString.includeFieldNames", "Include the field names in the generated toString method (default = true).") {}; + /** + * lombok configuration: {@code lombok.toString.onlyExplicitlyIncluded} = {@code true} | {@code false}. + * + * 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<Boolean> TO_STRING_ONLY_EXPLICITLY_INCLUDED = new ConfigurationKey<Boolean>("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/core/AST.java b/src/core/lombok/core/AST.java index 07d035c5..738241e2 100755 --- a/src/core/lombok/core/AST.java +++ b/src/core/lombok/core/AST.java @@ -454,4 +454,9 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, if (configTracker != null) configTracker.end(start); } } + + public boolean getBooleanAnnotationValue(AnnotationValues<?> annotation, String annoMethod, ConfigurationKey<Boolean> confKey) { + Boolean conf = readConfiguration(confKey); + return annotation.isExplicit(annoMethod) || conf == null ? annotation.getAsBoolean(annoMethod) : conf; + } } diff --git a/src/core/lombok/core/handlers/InclusionExclusionUtils.java b/src/core/lombok/core/handlers/InclusionExclusionUtils.java index 0aa6c47b..2a519b82 100644 --- a/src/core/lombok/core/handlers/InclusionExclusionUtils.java +++ b/src/core/lombok/core/handlers/InclusionExclusionUtils.java @@ -113,10 +113,14 @@ public class InclusionExclusionUtils { } private 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, boolean includeTransient) { + boolean onlyExplicitlyIncluded = annotation != null ? annotation.getAsBoolean("onlyExplicitlyIncluded") : false; + return handleIncludeExcludeMarking(inclType, onlyExplicitlyIncluded, replaceName, exclType, typeNode, annotation, annotationNode, includeTransient); + } + + private 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, boolean onlyExplicitlyIncluded, String replaceName, Class<? extends Annotation> exclType, LombokNode<A, L, N> typeNode, AnnotationValues<?> annotation, LombokNode<A, L, N> annotationNode, boolean includeTransient) { 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 = annotation != null ? annotation.getAsBoolean("onlyExplicitlyIncluded") : false; boolean memberAnnotationMode = onlyExplicitlyIncluded; List<Included<L, I>> members = new ArrayList<Included<L, I>>(); List<String> namesToAutoExclude = new ArrayList<String>(); @@ -203,14 +207,14 @@ public class InclusionExclusionUtils { 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, true); + 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, boolean onlyExplicitlyIncluded, AnnotationValues<ToString> annotation, LombokNode<A, L, N> annotationNode) { + List<Included<L, ToString.Include>> members = handleIncludeExcludeMarking(ToString.Include.class, onlyExplicitlyIncluded, "name", ToString.Exclude.class, typeNode, annotation, annotationNode, true); 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(); - + return compareRankOrPosition(ra, rb, a.getNode(), b.getNode()); } }); @@ -219,28 +223,28 @@ public class InclusionExclusionUtils { 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) { List<Included<L, EqualsAndHashCode.Include>> members = handleIncludeExcludeMarking(EqualsAndHashCode.Include.class, "replaces", EqualsAndHashCode.Exclude.class, typeNode, annotation, annotationNode, false); - + Collections.sort(members, new Comparator<Included<L, EqualsAndHashCode.Include>>() { @Override public int compare(Included<L, EqualsAndHashCode.Include> a, Included<L, EqualsAndHashCode.Include> b) { int ra = a.hasExplicitRank() ? a.getInc().rank() : HandlerUtil.defaultEqualsAndHashcodeIncludeRank(a.node.fieldOrMethodBaseType()); int rb = b.hasExplicitRank() ? b.getInc().rank() : HandlerUtil.defaultEqualsAndHashcodeIncludeRank(b.node.fieldOrMethodBaseType()); - + return compareRankOrPosition(ra, rb, a.getNode(), b.getNode()); } }); return members; } - + private static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> int compareRankOrPosition(int ra, int rb, LombokNode<A, L, N> nodeA, LombokNode<A, L, N> nodeB) { if (ra < rb) return +1; if (ra > rb) return -1; - + int pa = nodeA.getStartPos(); int pb = nodeB.getStartPos(); - + if (pa < pb) return -1; if (pa > pb) return +1; - + return 0; } } diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index 05b0e069..6beaa848 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -76,7 +76,8 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString"); ToString ann = annotation.getInstance(); - List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); + boolean onlyExplicitlyIncluded = annotationNode.getAst().getBooleanAnnotationValue(annotation, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); + List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), onlyExplicitlyIncluded, annotation, annotationNode); if (members == null) return; Boolean callSuper = ann.callSuper(); @@ -99,16 +100,14 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { return; } - boolean includeFieldNames = true; - try { - Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); - } catch (Exception ignore) {} + AnnotationValues<ToString> anno = AnnotationValues.of(ToString.class); + boolean includeFieldNames = typeNode.getAst().getBooleanAnnotationValue(anno, "includeFieldNames", ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + boolean onlyExplicitlyIncluded = typeNode.getAst().getBooleanAnnotationValue(anno, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); + List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, onlyExplicitlyIncluded, null, null); generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 249993ee..8a0bc686 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -65,7 +65,8 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { deleteAnnotationIfNeccessary(annotationNode, ToString.class); ToString ann = annotation.getInstance(); - java.util.List<Included<JavacNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode); + boolean onlyExplicitlyIncluded = annotationNode.getAst().getBooleanAnnotationValue(annotation, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); + java.util.List<Included<JavacNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), onlyExplicitlyIncluded, annotation, annotationNode); if (members == null) return; Boolean callSuper = ann.callSuper(); @@ -76,10 +77,9 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - boolean includeNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration; + boolean includeFieldNames = annotationNode.getAst().getBooleanAnnotationValue(annotation, "includeFieldNames", ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - generateToString(annotationNode.up(), annotationNode, members, includeNames, callSuper, true, fieldAccess); + generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess); } public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) { @@ -88,16 +88,14 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { return; } - boolean includeFieldNames = true; - try { - Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); - includeFieldNames = configuration != null ? configuration : ((Boolean) ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); - } catch (Exception ignore) {} + AnnotationValues<ToString> anno = AnnotationValues.of(ToString.class); + boolean includeFieldNames = typeNode.getAst().getBooleanAnnotationValue(anno, "includeFieldNames", ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + boolean onlyExplicitlyIncluded = typeNode.getAst().getBooleanAnnotationValue(anno, "onlyExplicitlyIncluded", ConfigurationKeys.TO_STRING_ONLY_EXPLICITLY_INCLUDED); Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - java.util.List<Included<JavacNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null); + java.util.List<Included<JavacNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, onlyExplicitlyIncluded, null, null); generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access); } diff --git a/test/transform/resource/after-delombok/ToStringExplicitIncludeConf.java b/test/transform/resource/after-delombok/ToStringExplicitIncludeConf.java new file mode 100644 index 00000000..a1bd8ed9 --- /dev/null +++ b/test/transform/resource/after-delombok/ToStringExplicitIncludeConf.java @@ -0,0 +1,9 @@ +class ToStringExplicitIncludeConf { + int x; + int y; + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "ToStringExplicitIncludeConf(" + this.y + ")"; + } +} diff --git a/test/transform/resource/after-ecj/ToStringExplicitIncludeConf.java b/test/transform/resource/after-ecj/ToStringExplicitIncludeConf.java new file mode 100644 index 00000000..a2b801c6 --- /dev/null +++ b/test/transform/resource/after-ecj/ToStringExplicitIncludeConf.java @@ -0,0 +1,10 @@ +@lombok.ToString class ToStringExplicitIncludeConf { + int x; + @lombok.ToString.Include int y; + ToStringExplicitIncludeConf() { + super(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ToStringExplicitIncludeConf(y=" + this.y) + ")"); + } +} diff --git a/test/transform/resource/before/ToStringExplicitIncludeConf.java b/test/transform/resource/before/ToStringExplicitIncludeConf.java new file mode 100644 index 00000000..ee49c754 --- /dev/null +++ b/test/transform/resource/before/ToStringExplicitIncludeConf.java @@ -0,0 +1,7 @@ +//CONF: lombok.toString.onlyExplicitlyIncluded = true + +@lombok.ToString +class ToStringExplicitIncludeConf { + int x; + @lombok.ToString.Include int y; +} diff --git a/website/templates/features/ToString.html b/website/templates/features/ToString.html index 456092d5..87e06649 100644 --- a/website/templates/features/ToString.html +++ b/website/templates/features/ToString.html @@ -33,6 +33,11 @@ </dt><dd> If set to <code>call</code>, lombok will generate calls to the superclass implementation of <code>toString</code> if your class extends something. If set to <code>skip</code> no such call is generated. If set to <code>warn</code> no such call is generated either, but lombok does generate a warning to tell you about it. </dd><dt> + <code>lombok.toString.onlyExplicitlyIncluded</code> = [<code>true</code> | <code>false</code>] (default: false) + </dt><dd> + If set to <code>false</code> (default), all fields (unless <code>static</code>, name starts with a dollar, or otherwise excluded for obvious reasons) serve as the default set of things to include in the toString, modifiable by using the <code>@ToString.Exclude</code> and <code>@ToString.Include</code> options. + If set to <code>true</code>, nothing is included unless explicitly marked with <code>@ToString.Include</code>. + </dd><dt> <code>lombok.toString.flagUsage</code> = [<code>warning</code> | <code>error</code>] (default: not set) </dt><dd> Lombok will flag any usage of <code>@ToString</code> as a warning or error if configured. |