Skip to content

Commit

Permalink
[fixes #2849] Make ToString's onlyExplicitlyIncluded a config key.
Browse files Browse the repository at this point in the history
  • Loading branch information
rzwitserloot committed Dec 21, 2021
1 parent d71a880 commit 6d2a474
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 27 deletions.
7 changes: 7 additions & 0 deletions src/core/lombok/ConfigurationKeys.java
Expand Up @@ -291,6 +291,13 @@ private 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 -----

/**
Expand Down
5 changes: 5 additions & 0 deletions src/core/lombok/core/AST.java
Expand Up @@ -454,4 +454,9 @@ public final <T> T readConfigurationOr(ConfigurationKey<T> key, T defaultValue)
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;
}
}
24 changes: 14 additions & 10 deletions src/core/lombok/core/handlers/InclusionExclusionUtils.java
Expand Up @@ -113,10 +113,14 @@ private static String innerAnnName(Class<? extends Annotation> type) {
}

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>();
Expand Down Expand Up @@ -203,14 +207,14 @@ private static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N, I exte
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());
}
});
Expand All @@ -219,28 +223,28 @@ public static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> List<In

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;
}
}
13 changes: 6 additions & 7 deletions src/core/lombok/eclipse/handlers/HandleToString.java
Expand Up @@ -76,7 +76,8 @@ public void handle(AnnotationValues<ToString> annotation, Annotation ast, Eclips
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();
Expand All @@ -99,16 +100,14 @@ public void generateToStringForType(EclipseNode typeNode, EclipseNode errorNode)
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);
}

Expand Down
18 changes: 8 additions & 10 deletions src/core/lombok/javac/handlers/HandleToString.java
Expand Up @@ -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();
Expand All @@ -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) {
Expand All @@ -88,16 +88,14 @@ public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) {
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);
}

Expand Down
@@ -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 + ")";
}
}
10 changes: 10 additions & 0 deletions 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) + ")");
}
}
@@ -0,0 +1,7 @@
//CONF: lombok.toString.onlyExplicitlyIncluded = true

@lombok.ToString
class ToStringExplicitIncludeConf {
int x;
@lombok.ToString.Include int y;
}
5 changes: 5 additions & 0 deletions website/templates/features/ToString.html
Expand Up @@ -32,6 +32,11 @@
<code>lombok.toString.callSuper</code> = [<code>call</code> | <code>skip</code> | <code>warn</code>] (default: skip)
</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>
Expand Down

0 comments on commit 6d2a474

Please sign in to comment.