This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 64d8350 GROOVY-10321: @ToString could provide annotation attributes
to control further the format of the created toString
64d8350 is described below
commit 64d83506103500476e5152932d1f912f16d28351
Author: Paul King <[email protected]>
AuthorDate: Fri Oct 22 18:36:11 2021 +1000
GROOVY-10321: @ToString could provide annotation attributes to control
further the format of the created toString
---
src/main/java/groovy/transform/ToString.java | 28 ++++++++++++++
.../transform/ToStringASTTransformation.java | 44 +++++++++++++---------
.../groovy/transform/ToStringTransformTest.groovy | 22 +++++++++++
3 files changed, 76 insertions(+), 18 deletions(-)
diff --git a/src/main/java/groovy/transform/ToString.java
b/src/main/java/groovy/transform/ToString.java
index 81e9f36..53ecb3f 100644
--- a/src/main/java/groovy/transform/ToString.java
+++ b/src/main/java/groovy/transform/ToString.java
@@ -363,4 +363,32 @@ public @interface ToString {
* @since 4.0.0
*/
boolean pojo() default false;
+
+ /**
+ * The String to use after the classname and before the list of
properties/fields.
+ *
+ * @since 4.0.0
+ */
+ String leftDelimiter() default "(";
+
+ /**
+ * The String to use after the list of properties/fields.
+ *
+ * @since 4.0.0
+ */
+ String rightDelimiter() default ")";
+
+ /**
+ * The string to use between the name of the property/field and its value
when {@code includeNames} is true.
+ *
+ * @since 4.0.0
+ */
+ String nameValueSeparator() default ":";
+
+ /**
+ * The string to use between each property/field.
+ *
+ * @since 4.0.0
+ */
+ String fieldSeparator() default ", ";
}
diff --git
a/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java
b/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java
index c1c3a1f..1924ea6 100644
--- a/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/ToStringASTTransformation.java
@@ -103,6 +103,10 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
boolean cacheToString = memberHasValue(anno, "cache", true);
List<String> excludes = getMemberStringList(anno, "excludes");
List<String> includes = getMemberStringList(anno, "includes");
+ String leftDelim = getMemberStringValue(anno, "leftDelimiter",
"(");
+ String rightDelim = getMemberStringValue(anno, "rightDelimiter",
")");
+ String nameValueSep = getMemberStringValue(anno,
"nameValueSeparator", ":");
+ String fieldSep = getMemberStringValue(anno, "fieldSeparator", ",
");
if (includes != null && includes.contains("super")) {
includeSuper = true;
}
@@ -127,7 +131,8 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes,
MY_TYPE_NAME)) return;
if (!checkPropertyList(cNode, includes != null ?
DefaultGroovyMethods.minus(includes, "super") : null, "includes", anno,
MY_TYPE_NAME, includeFields, includeSuperProperties, allProperties)) return;
if (!checkPropertyList(cNode, excludes, "excludes", anno,
MY_TYPE_NAME, includeFields, includeSuperProperties, allProperties)) return;
- createToString(cNode, includeSuper, includeFields, excludes,
includes, includeNames, ignoreNulls, includePackage, cacheToString,
includeSuperProperties, allProperties, allNames, includeSuperFields, pojo);
+ String[] delims = new String[]{leftDelim, rightDelim,
nameValueSep, fieldSep};
+ createToString(cNode, includeSuper, includeFields, excludes,
includes, includeNames, ignoreNulls, includePackage, cacheToString,
includeSuperProperties, allProperties, allNames, includeSuperFields, pojo,
delims);
}
}
@@ -156,10 +161,13 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
}
public static void createToString(ClassNode cNode, boolean includeSuper,
boolean includeFields, List<String> excludes, List<String> includes, boolean
includeNames, boolean ignoreNulls, boolean includePackage, boolean cache,
boolean includeSuperProperties, boolean allProperties, boolean allNames,
boolean includeSuperFields) {
- createToString(cNode, includeSuper, includeFields, excludes, includes,
includeNames, ignoreNulls, includePackage, cache, includeSuperProperties,
allProperties, allNames, includeSuperFields, false);
+ createToString(cNode, includeSuper, includeFields, excludes, includes,
includeNames, ignoreNulls, includePackage, cache, includeSuperProperties,
allProperties, allNames, includeSuperFields, false, null);
}
- public static void createToString(ClassNode cNode, boolean includeSuper,
boolean includeFields, List<String> excludes, List<String> includes, boolean
includeNames, boolean ignoreNulls, boolean includePackage, boolean cache,
boolean includeSuperProperties, boolean allProperties, boolean allNames,
boolean includeSuperFields, boolean pojo) {
+ public static void createToString(ClassNode cNode, boolean includeSuper,
boolean includeFields, List<String> excludes, List<String> includes, boolean
includeNames, boolean ignoreNulls, boolean includePackage, boolean cache,
boolean includeSuperProperties, boolean allProperties, boolean allNames,
boolean includeSuperFields, boolean pojo, String[] delims) {
+ if (delims == null || delims.length != 4) {
+ delims = new String[]{"(", ")", ":", ", "};
+ }
// make a public method if none exists otherwise try a private method
with leading underscore
boolean hasExistingToString = hasDeclaredMethod(cNode, TO_STRING, 0);
if (hasExistingToString) {
@@ -177,11 +185,11 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
final Expression savedToString = varX(cacheField);
body.addStatement(ifS(
equalsNullX(savedToString),
- assignS(savedToString, calculateToStringStatements(cNode,
includeSuper, includeFields, includeSuperFields, excludes, includes,
includeNames, ignoreNulls, includePackage, includeSuperProperties,
allProperties, body, allNames, pojo))
+ assignS(savedToString, calculateToStringStatements(cNode,
includeSuper, includeFields, includeSuperFields, excludes, includes,
includeNames, ignoreNulls, includePackage, includeSuperProperties,
allProperties, body, allNames, pojo, delims))
));
tempToString = savedToString;
} else {
- tempToString = calculateToStringStatements(cNode, includeSuper,
includeFields, includeSuperFields, excludes, includes, includeNames,
ignoreNulls, includePackage, includeSuperProperties, allProperties, body,
allNames, pojo);
+ tempToString = calculateToStringStatements(cNode, includeSuper,
includeFields, includeSuperFields, excludes, includes, includeNames,
ignoreNulls, includePackage, includeSuperProperties, allProperties, body,
allNames, pojo, delims);
}
body.addStatement(returnS(tempToString));
@@ -201,7 +209,7 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
boolean canBeSelf;
}
- private static Expression calculateToStringStatements(ClassNode cNode,
boolean includeSuper, boolean includeFields, boolean includeSuperFields,
List<String> excludes, final List<String> includes, boolean includeNames,
boolean ignoreNulls, boolean includePackage, boolean includeSuperProperties,
boolean allProperties, BlockStatement body, boolean allNames, boolean pojo) {
+ private static Expression calculateToStringStatements(ClassNode cNode,
boolean includeSuper, boolean includeFields, boolean includeSuperFields,
List<String> excludes, final List<String> includes, boolean includeNames,
boolean ignoreNulls, boolean includePackage, boolean includeSuperProperties,
boolean allProperties, BlockStatement body, boolean allNames, boolean pojo,
String[] delims) {
// def _result = new StringBuilder()
final Expression result = localVarX("_result");
body.addStatement(declS(result, ctorX(STRINGBUILDER_TYPE)));
@@ -213,7 +221,7 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
// <class_name>(
String className = (includePackage) ? cNode.getName() :
cNode.getNameWithoutPackage();
- body.addStatement(appendS(result, constX(className + "(")));
+ body.addStatement(appendS(result, constX(className + delims[0])));
Set<String> names = new HashSet<>();
List<PropertyNode> superList;
@@ -250,20 +258,20 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
}
for (ToStringElement el : elements) {
- appendValue(body, result, first, el.value, el.name, includeNames,
ignoreNulls, el.canBeSelf, pojo);
+ appendValue(body, result, first, el.value, el.name, includeNames,
ignoreNulls, el.canBeSelf, pojo, delims);
}
// wrap up
- body.addStatement(appendS(result, constX(")")));
+ body.addStatement(appendS(result, constX(delims[1])));
return toStringX(result);
}
- private static void appendValue(BlockStatement body, Expression result,
VariableExpression first, Expression value, String name, boolean includeNames,
boolean ignoreNulls, boolean canBeSelf, boolean pojo) {
+ private static void appendValue(BlockStatement body, Expression result,
VariableExpression first, Expression value, String name, boolean includeNames,
boolean ignoreNulls, boolean canBeSelf, boolean pojo, String[] delims) {
final BlockStatement thenBlock = new BlockStatement();
final Statement appendValue = ignoreNulls ? ifS(notNullX(value),
thenBlock) : thenBlock;
- appendCommaIfNotFirst(thenBlock, result, first);
- appendPrefix(thenBlock, result, name, includeNames);
+ appendCommaIfNotFirst(thenBlock, result, first, delims);
+ appendPrefix(thenBlock, result, name, includeNames, delims);
Expression toString = pojo ? toStringX(value) : callX(INVOKER_TYPE,
TO_STRING, value);
if (canBeSelf) {
thenBlock.addStatement(ifElseS(
@@ -280,21 +288,21 @@ public class ToStringASTTransformation extends
AbstractASTTransformation {
return
StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(valueType, cNode);
}
- private static void appendCommaIfNotFirst(BlockStatement body, Expression
result, VariableExpression first) {
+ private static void appendCommaIfNotFirst(BlockStatement body, Expression
result, VariableExpression first, String[] delims) {
// if ($toStringFirst) $toStringFirst = false else result.append(", ")
body.addStatement(ifElseS(
first,
assignS(first, ConstantExpression.FALSE),
- appendS(result, constX(", "))));
+ appendS(result, constX(delims[3]))));
}
- private static void appendPrefix(BlockStatement body, Expression result,
String name, boolean includeNames) {
- if (includeNames) body.addStatement(toStringPropertyName(result,
name));
+ private static void appendPrefix(BlockStatement body, Expression result,
String name, boolean includeNames, String[] delims) {
+ if (includeNames) body.addStatement(toStringPropertyName(result, name,
delims));
}
- private static Statement toStringPropertyName(Expression result, String
fName) {
+ private static Statement toStringPropertyName(Expression result, String
fName, String[] delims) {
final BlockStatement body = new BlockStatement();
- body.addStatement(appendS(result, constX(fName + ":")));
+ body.addStatement(appendS(result, constX(fName + delims[2])));
return body;
}
}
diff --git
a/src/test/org/codehaus/groovy/transform/ToStringTransformTest.groovy
b/src/test/org/codehaus/groovy/transform/ToStringTransformTest.groovy
index a94f26c..12eb461 100644
--- a/src/test/org/codehaus/groovy/transform/ToStringTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/ToStringTransformTest.groovy
@@ -450,6 +450,28 @@ class ToStringTransformTest extends GroovyShellTestCase {
'''
}
+ void testToStringFormatting_Groovy10231() {
+ def toString = evaluate("""
+ package pkg
+ import groovy.transform.*
+
+ @ToString
+ class Foo {
+ String baz = 'FooBaz'
+ }
+
+ @ToString(leftDelimiter="[", rightDelimiter="]",
nameValueSeparator="=", includeNames=true, includePackage=false)
+ class Bar {
+ String baz = 'BarBaz'
+ }
+
+ [new Foo(), new Bar()].toString()
+ """)
+
+ assert toString.contains('[pkg.Foo(FooBaz), Bar[baz=BarBaz]]')
+
+ }
+
void testIncludesWithSuper_Groovy8011() {
def toString = evaluate("""
import groovy.transform.*