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.*

Reply via email to