[ 
https://issues.apache.org/jira/browse/GROOVY-11893?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18071821#comment-18071821
 ] 

ASF GitHub Bot commented on GROOVY-11893:
-----------------------------------------

Copilot commented on code in PR #2424:
URL: https://github.com/apache/groovy/pull/2424#discussion_r3048327127


##########
src/main/java/org/codehaus/groovy/runtime/FormatHelper.java:
##########
@@ -61,8 +59,25 @@ private FormatHelper() {}
     private static final int ITEM_ALLOCATE_SIZE = 5;
 
     public static final MetaClassRegistry metaRegistry = 
GroovySystem.getMetaClassRegistry();
-    private static final String XMLUTIL_CLASS_FULL_NAME = "groovy.xml.XmlUtil";
-    private static final String SERIALIZE_METHOD_NAME = "serialize";
+    private static final String GROOVY_TO_STRING = "groovyToString";
+    private static final Class[] EMPTY_TYPES = {};
+
+    /**
+     * Attempts to invoke a {@code groovyToString()} extension method on the 
given object
+     * via the metaclass. Returns the result if found, or {@code null} if no 
such method exists.
+     */
+    static String tryGroovyToString(Object object) {
+        if (object == null) return null;
+        try {
+            MetaMethod method = 
InvokerHelper.getMetaClass(object).getMetaMethod(GROOVY_TO_STRING, EMPTY_TYPES);
+            if (method != null) {
+                return (String) method.invoke(object, EMPTY_ARGS);
+            }
+        } catch (ClassCastException | GroovyRuntimeException ignore) {
+            // method found but not applicable to this object's actual type
+        }

Review Comment:
   `tryGroovyToString` currently catches `GroovyRuntimeException` and returns 
`null`, which will silently fall back to `toString()` even when `safe` is 
false. This can hide real failures from user-provided `groovyToString()` 
implementations and makes the `safe` flag ineffective for this code path. 
Consider only swallowing exceptions when `safe` is enabled (e.g., pass `safe` 
into `tryGroovyToString`) or rethrow runtime exceptions so unsafe formatting 
preserves the current fail-fast behavior.



##########
src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java:
##########
@@ -8144,6 +8144,101 @@ public static <G, K, V> Map<G, List<Map.Entry<K, V>>> 
groupEntriesBy(Map<K, V> s
         return answer;
     }
 
+    
//--------------------------------------------------------------------------
+    // groovyToString
+
+    /**
+     * Returns Groovy's default string representation for a Map.
+     * This is used by Groovy's formatting infrastructure (e.g., GString 
interpolation,
+     * {@code println}, assert messages). By default, it delegates to
+     * {@link FormatHelper#toMapString(Map)}.
+     * <p>
+     * <pre class="groovyTestCase">
+     * assert [a:1, b:2].groovyToString() == '[a:1, b:2]'
+     * </pre>
+     * <p>
+     * If disabled, e.g. via {@code 
-Dgroovy.extension.disable=groovyToString(Map)},
+     * the normal map {@code toString()} is used instead.
+     * Alternatively, you have the option to provide a replacement extension 
method in an extension module
+     * to customize how maps are displayed throughout Groovy.
+     *
+     * @param self the Map to format
+     * @return the string representation
+     * @since 6.0.0
+     */
+    public static String groovyToString(Map self) {
+        return FormatHelper.toMapString(self);
+    }
+
+    /**
+     * Returns Groovy's default string representation for a Range.
+     * By default, it delegates to {@link Range#toString()},
+     * producing the compact {@code from..to} notation.
+     * <p>
+     * <pre class="groovyTestCase">
+     * assert (1..4).groovyToString() == '1..4'
+     * </pre>
+     * <p>
+     * This method exists to stop the {@code groovyToString(Collection)} 
variant
+     * from overriding the built-in {@code Range#toString}.
+     * Since a range is a list, you can use {@code range.toListString()}
+     * to print it using normal list formatting.
+     *
+     * @param self the Range to format
+     * @return the string representation
+     * @since 6.0.0
+     */
+    public static String groovyToString(Range self) {
+        return self.toString();
+    }
+
+    /**
+     * Returns Groovy's default string representation for a Collection.
+     * This is used by Groovy's formatting infrastructure (e.g., GString 
interpolation,
+     * {@code println}, assert messages). By default, it delegates to
+     * {@link FormatHelper#toListString(Collection)}.
+     * <p>
+     * <pre class="groovyTestCase">
+     * assert [1, 2, 3].groovyToString() == '[1, 2, 3]'
+     * </pre>
+     * <p>
+     * If disabled, e.g. via {@code 
-Dgroovy.extension.disable=groovyToString(Collection)},
+     * the normal list {@code toString()} is used instead.
+     * Alternatively, you have the option to provide a replacement extension 
method in an extension module
+     * to customize how collections are displayed throughout Groovy.
+     *
+     * @param self the Collection to format
+     * @return the string representation
+     * @since 6.0.0
+     */
+    public static String groovyToString(Collection self) {
+        return FormatHelper.toListString(self);
+    }
+
+    /**
+     * Returns Groovy's default string representation for an XML Element.
+     * This is used by Groovy's formatting infrastructure. By default, it
+     * serializes the element using {@code 
groovy.xml.XmlUtil.serialize(Element)}.
+     * <p>
+     * If disabled, e.g. via {@code 
-Dgroovy.extension.disable=groovyToString(Element)},
+     * the element's default {@code toString()} is used instead.
+     * Alternatively, you have the option to provide a replacement extension 
method in an extension module
+     * to customize how elements are displayed throughout Groovy.
+     *
+     * @param self the Element to format
+     * @return the serialized XML string
+     * @since 6.0.0
+     */
+    public static String groovyToString(org.w3c.dom.Element self) {
+        try {
+            java.lang.reflect.Method serialize = 
Class.forName("groovy.xml.XmlUtil")
+                    .getMethod("serialize", org.w3c.dom.Element.class);
+            return (String) serialize.invoke(null, self);
+        } catch (Exception e) {
+            return self.toString();
+        }

Review Comment:
   New `groovyToString(...)` extension methods are introduced as part of 
Groovy’s core formatting hook, but there don’t appear to be corresponding tests 
asserting (1) that `FormatHelper.format`/interpolation/println pick these 
methods up for Map/Collection/Range/Element, and (2) that 
`-Dgroovy.extension.disable=groovyToString` (and receiver-specific variants) 
reliably falls back to the JDK `toString()` behavior. Adding focused tests 
would help prevent regressions in this new formatting contract.





> Standardise hooks for Groovy pretty printing
> --------------------------------------------
>
>                 Key: GROOVY-11893
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11893
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>
> Groovy has special hard-coded handling when printing lists, maps and in some 
> cases arrays. There have been proposals for disabling this, e.g. part of:
> https://github.com/apache/groovy/pull/566
> This issue is to look at a more general approach to handling such cases 
> including the ability to disable the special handling and fall back to JDK 
> default formatting for e.g. maps.
> The idea that will be explored is to have groovyToString extension methods. 
> They would encode the current hard-coded formatting behavior. These 
> extensions could be disabled using the groovy.extension.disable system 
> property to fall back to JDK defaults.
> Note that currently not all paths funnel through a single path that we can 
> fix using this mechanism. Printing some objects through inspect does things 
> like limit length and escape backslashes etc. Currently, we don't try to 
> force those through this mechanism.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to