You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2022/06/23 13:59:01 UTC

[groovy] branch master updated (b5f8f5dff8 -> e2ee8414b8)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


    from b5f8f5dff8 GROOVY-10660: STC: infer closure parameter types from method return type
     new c02de25be9 GROOVY-10661: Show path to current level object and support ALT-double click
     new bfb89e0ec0 GROOVY-10661: reduce some duplication in ObjectBrowser
     new e2ee8414b8 GROOVY-10661: reduce some duplication in Inspector

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/main/java/groovy/inspect/Inspector.java        |  93 +++++----------
 .../groovy/groovy/console/ui/ObjectBrowser.groovy  | 131 ++++++++++-----------
 2 files changed, 94 insertions(+), 130 deletions(-)


[groovy] 03/03: GROOVY-10661: reduce some duplication in Inspector

Posted by pa...@apache.org.
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

commit e2ee8414b8e78612e1c5034329d24146180e6154
Author: Paul King <pa...@asert.com.au>
AuthorDate: Thu Jun 23 23:58:52 2022 +1000

    GROOVY-10661: reduce some duplication in Inspector
---
 src/main/java/groovy/inspect/Inspector.java        | 93 +++++++---------------
 .../groovy/groovy/console/ui/ObjectBrowser.groovy  | 26 +++---
 2 files changed, 43 insertions(+), 76 deletions(-)

diff --git a/src/main/java/groovy/inspect/Inspector.java b/src/main/java/groovy/inspect/Inspector.java
index 8833027d4a..b435c25a37 100644
--- a/src/main/java/groovy/inspect/Inspector.java
+++ b/src/main/java/groovy/inspect/Inspector.java
@@ -22,6 +22,7 @@ import groovy.lang.GroovyObject;
 import groovy.lang.MetaClass;
 import groovy.lang.MetaMethod;
 import groovy.lang.PropertyValue;
+import groovy.lang.Tuple2;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 import org.codehaus.groovy.runtime.FormatHelper;
 import org.codehaus.groovy.runtime.InvokerHelper;
@@ -60,7 +61,6 @@ public class Inspector {
     public static final int MEMBER_PARAMS_IDX = 5;
     public static final int MEMBER_VALUE_IDX = 5;
     public static final int MEMBER_EXCEPTIONS_IDX = 6;
-    public static final int MEMBER_RAW_VALUE_IDX = 6;
 
     public static final String NOT_APPLICABLE = "n/a";
     public static final String GROOVY = "GROOVY";
@@ -197,48 +197,35 @@ public class Inspector {
     }
 
     protected String[] fieldInfo(PropertyValue pv) {
-        String[] result = new String[MEMBER_VALUE_IDX + 1];
-        result[MEMBER_ORIGIN_IDX] = GROOVY;
-        result[MEMBER_MODIFIER_IDX] = "public";
-        result[MEMBER_DECLARER_IDX] = NOT_APPLICABLE;
-        result[MEMBER_TYPE_IDX] = shortName(pv.getType());
-        result[MEMBER_NAME_IDX] = pv.getName();
-        try {
-            result[MEMBER_VALUE_IDX] = FormatHelper.inspect(pv.getValue());
-        } catch (Exception e) {
-            result[MEMBER_VALUE_IDX] = NOT_APPLICABLE;
-        }
-        return withoutNulls(result);
+        return fieldWithInfo(pv).getV2();
     }
 
-    public Object[] getPropertyInfoWithRawValue() {
-        List props = DefaultGroovyMethods.getMetaPropertyValues(objectUnderInspection);
+    public Object[] getPropertiesWithInfo() {
+        List<PropertyValue> props = DefaultGroovyMethods.getMetaPropertyValues(objectUnderInspection);
         Object[] result = new Object[props.size()];
         int i = 0;
-        for (Iterator iter = props.iterator(); iter.hasNext(); i++) {
-            PropertyValue pv = (PropertyValue) iter.next();
-            result[i] = fieldInfoWithRawValue(pv);
+        for (Iterator<PropertyValue> iter = props.iterator(); iter.hasNext(); i++) {
+            result[i] = fieldWithInfo(iter.next());
         }
         return result;
     }
 
-    protected Object[] fieldInfoWithRawValue(PropertyValue pv) {
-        Object[] result = new Object[MEMBER_VALUE_IDX + 2];
-        result[MEMBER_ORIGIN_IDX] = GROOVY;
-        result[MEMBER_MODIFIER_IDX] = "public";
-        result[MEMBER_DECLARER_IDX] = NOT_APPLICABLE;
-        result[MEMBER_TYPE_IDX] = shortName(pv.getType());
-        result[MEMBER_NAME_IDX] = pv.getName();
-        Object rawValue = null;
+    protected Tuple2<Object, String[]> fieldWithInfo(PropertyValue pv) {
+        String[] info = new String[MEMBER_VALUE_IDX + 1];
+        info[MEMBER_ORIGIN_IDX] = GROOVY;
+        info[MEMBER_MODIFIER_IDX] = "public";
+        info[MEMBER_DECLARER_IDX] = NOT_APPLICABLE;
+        info[MEMBER_TYPE_IDX] = shortName(pv.getType());
+        info[MEMBER_NAME_IDX] = pv.getName();
+        Object field = null;
         try {
-            result[MEMBER_VALUE_IDX] = FormatHelper.inspect(pv.getValue());
-            rawValue = pv.getValue();
+            field = pv.getValue();
+            info[MEMBER_VALUE_IDX] = FormatHelper.inspect(field);
         } catch (Exception e) {
-            result[MEMBER_VALUE_IDX] = NOT_APPLICABLE;
+            info[MEMBER_VALUE_IDX] = NOT_APPLICABLE;
         }
-        result = withoutNullsWithRawValue(result);
-        result[MEMBER_RAW_VALUE_IDX] = rawValue;
-        return result;
+        info = withoutNulls(info);
+        return new Tuple2<>(field, info);
     }
 
     protected Class getClassUnderInspection() {
@@ -321,15 +308,6 @@ public class Inspector {
         return toNormalize;
     }
 
-    protected Object[] withoutNullsWithRawValue(Object[] toNormalize) {
-        for (int i = 0; i < toNormalize.length; i++) {
-            if (toNormalize[i] == null) {
-                toNormalize[i] = NOT_APPLICABLE;
-            }
-        }
-        return toNormalize;
-    }
-
     public static void print(Object[] memberInfo) {
         print(System.out, memberInfo);
     }
@@ -346,7 +324,11 @@ public class Inspector {
     }
 
     public static Collection sort(List<Object> memberInfo) {
-        memberInfo.sort(new MemberComparator());
+        return sort(memberInfo, new MemberComparator());
+    }
+
+    public static Collection sort(List<Object> memberInfo, Comparator<Object> comparator) {
+        memberInfo.sort(comparator);
         return memberInfo;
     }
 
@@ -372,30 +354,15 @@ public class Inspector {
         }
     }
 
-    public static Collection sortWithRawValue(List<Object> memberInfo) {
-        memberInfo.sort(new MemberComparatorWithRawValue());
-        return memberInfo;
-    }
-
-    public static class MemberComparatorWithRawValue implements Comparator<Object>, Serializable {
-        private static final long serialVersionUID = -7691851726606749542L;
+    public static class MemberComparatorWithValue implements Comparator<Object>, Serializable {
+        private static final long serialVersionUID = 294298614093394525L;
+        private static final MemberComparator delegate = new MemberComparator();
 
         @Override
         public int compare(Object a, Object b) {
-            Object[] aStr = (Object[]) a;
-            Object[] bStr = (Object[]) b;
-            int result = ((String) aStr[Inspector.MEMBER_NAME_IDX]).compareTo((String) bStr[Inspector.MEMBER_NAME_IDX]);
-            if (0 != result) return result;
-            result = ((String) aStr[Inspector.MEMBER_TYPE_IDX]).compareTo((String) bStr[Inspector.MEMBER_TYPE_IDX]);
-            if (0 != result) return result;
-            result = ((String)aStr[Inspector.MEMBER_PARAMS_IDX]).compareTo((String) bStr[Inspector.MEMBER_PARAMS_IDX]);
-            if (0 != result) return result;
-            result =((String) aStr[Inspector.MEMBER_DECLARER_IDX]).compareTo((String) bStr[Inspector.MEMBER_DECLARER_IDX]);
-            if (0 != result) return result;
-            result = ((String)aStr[Inspector.MEMBER_MODIFIER_IDX]).compareTo((String) bStr[Inspector.MEMBER_MODIFIER_IDX]);
-            if (0 != result) return result;
-            result = ((String) aStr[Inspector.MEMBER_ORIGIN_IDX]).compareTo((String) bStr[Inspector.MEMBER_ORIGIN_IDX]);
-            return result;
+            Tuple2<Object, String[]> aTuple = (Tuple2<Object, String[]>) a;
+            Tuple2<Object, String[]> bTuple = (Tuple2<Object, String[]>) b;
+            return delegate.compare(aTuple.getV2(), bTuple.getV2());
         }
     }
 }
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
index 40c2630adf..315c3c5be1 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
@@ -37,7 +37,6 @@ import static groovy.inspect.Inspector.MEMBER_ORIGIN_IDX
 import static groovy.inspect.Inspector.MEMBER_PARAMS_IDX
 import static groovy.inspect.Inspector.MEMBER_TYPE_IDX
 import static groovy.inspect.Inspector.MEMBER_VALUE_IDX
-import static groovy.inspect.Inspector.MEMBER_RAW_VALUE_IDX
 
 /**
  * A little GUI to show some of the Inspector capabilities.
@@ -52,6 +51,7 @@ class ObjectBrowser {
     def inspector
     def path
     def swing, frame, fieldTable, methodTable, arrayTable, collectionTable, mapTable
+    private static final Comparator<Object> comparator = new Inspector.MemberComparatorWithValue()
 
     static void main(args) {
         inspect('some String')
@@ -96,7 +96,7 @@ class ObjectBrowser {
                                 tableModel(list: inspector.object.toList().withIndex()) {
                                     closureColumn(header: 'Index', read: { it[1] })
                                     closureColumn(header: 'Value', read: { it[0] })
-                                    closureColumn(header: 'Raw Value', read: { it[0] }) // to support sorting
+                                    closureColumn(header: 'Raw Value', read: { it[0] })
                                 }
                             }
                             arrayTable.columnModel.getColumn(2).with {
@@ -114,7 +114,7 @@ class ObjectBrowser {
                                 tableModel(list: inspector.object.withIndex()) {
                                     closureColumn(header: 'Index', read: { it[1] })
                                     closureColumn(header: 'Value', read: { it[0] })
-                                    closureColumn(header: 'Raw Value', read: { it[0] }) // to support sorting
+                                    closureColumn(header: 'Raw Value', read: { it[0] })
                                 }
                             }
                             collectionTable.columnModel.getColumn(2).with {
@@ -133,7 +133,7 @@ class ObjectBrowser {
                                     closureColumn(header: 'Index', read: { it[1] })
                                     closureColumn(header: 'Key', read: { it[0].key })
                                     closureColumn(header: 'Value', read: { it[0].value })
-                                    closureColumn(header: 'Raw Value', read: { it[0].value }) // to support sorting
+                                    closureColumn(header: 'Raw Value', read: { it[0].value })
                                 }
                             }
                             mapTable.columnModel.getColumn(3).with {
@@ -147,16 +147,16 @@ class ObjectBrowser {
                         }
                     }
                     scrollPane(name: ' Properties (includes public fields) ') {
-                        def data = Inspector.sortWithRawValue(inspector.propertyInfoWithRawValue.toList())
+                        def data = Inspector.sort(inspector.propertiesWithInfo.toList(), comparator)
                         fieldTable = table(selectionMode: SINGLE_SELECTION) {
                             tableModel(list: data) {
-                                closureColumn(header: 'Name', read: { it[MEMBER_NAME_IDX] })
-                                closureColumn(header: 'Value', read: { it[MEMBER_VALUE_IDX] })
-                                closureColumn(header: 'Type', read: { it[MEMBER_TYPE_IDX] })
-                                closureColumn(header: 'Origin', read: { it[MEMBER_ORIGIN_IDX] })
-                                closureColumn(header: 'Modifier', read: { it[MEMBER_MODIFIER_IDX] })
-                                closureColumn(header: 'Declarer', read: { it[MEMBER_DECLARER_IDX] })
-                                closureColumn(header: 'Raw Value', read: { it[MEMBER_RAW_VALUE_IDX] }) // to support sorting
+                                closureColumn(header: 'Name', read: { it.v2[MEMBER_NAME_IDX] })
+                                closureColumn(header: 'Value', read: { it.v2[MEMBER_VALUE_IDX] })
+                                closureColumn(header: 'Type', read: { it.v2[MEMBER_TYPE_IDX] })
+                                closureColumn(header: 'Origin', read: { it.v2[MEMBER_ORIGIN_IDX] })
+                                closureColumn(header: 'Modifier', read: { it.v2[MEMBER_MODIFIER_IDX] })
+                                closureColumn(header: 'Declarer', read: { it.v2[MEMBER_DECLARER_IDX] })
+                                closureColumn(header: 'Raw Value', read: { it.v1 })
                             }
                         }
                         fieldTable.columnModel.getColumn(6).with {
@@ -164,7 +164,7 @@ class ObjectBrowser {
                             maxWidth = 0
                             width = 0
                         }
-                        fieldTable.addMouseListener(makeClickAdapter(fieldTable, MEMBER_RAW_VALUE_IDX) { row ->
+                        fieldTable.addMouseListener(makeClickAdapter(fieldTable, 6) { row ->
                             path + (path.size() == 0 ? '' : '.') + "${fieldTable.model.getValueAt(row, 0)}"
                         })
                     }


[groovy] 01/03: GROOVY-10661: Show path to current level object and support ALT-double click

Posted by pa...@apache.org.
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

commit c02de25be9db6ca43a4cab65b1414049d79782bd
Author: Sandip Chitale <sa...@gmail.com>
AuthorDate: Mon Jun 20 18:12:52 2022 -0700

    GROOVY-10661: Show path to current level object and support ALT-double click
---
 .../groovy/groovy/console/ui/ObjectBrowser.groovy  | 50 +++++++++++++++++-----
 1 file changed, 39 insertions(+), 11 deletions(-)

diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
index 198ac3eb35..fcd4fda663 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
@@ -27,6 +27,8 @@ import java.awt.FlowLayout
 import java.awt.event.MouseAdapter
 import java.awt.event.MouseEvent
 
+import static javax.swing.ListSelectionModel.SINGLE_SELECTION
+
 import static groovy.inspect.Inspector.MEMBER_DECLARER_IDX
 import static groovy.inspect.Inspector.MEMBER_EXCEPTIONS_IDX
 import static groovy.inspect.Inspector.MEMBER_MODIFIER_IDX
@@ -48,15 +50,23 @@ import static groovy.inspect.Inspector.MEMBER_RAW_VALUE_IDX
 class ObjectBrowser {
 
     def inspector
+    def path
     def swing, frame, fieldTable, methodTable, arrayTable, collectionTable, mapTable
 
     static void main(args) {
         inspect('some String')
     }
 
-    static void inspect(objectUnderInspection) {
-        def browser = new ObjectBrowser()
+    public ObjectBrowser(String path = '') {
+        this.path = (path == null ? '' : path)
+    }
+
+    static void inspect(objectUnderInspection, String path = '') {
+        def browser = new ObjectBrowser(path)
         browser.inspector = new Inspector(objectUnderInspection)
+        if (objectUnderInspection != null && browser.path == '') {
+            browser.path = "${objectUnderInspection.getClass().name} instance"
+        }
         browser.run()
     }
 
@@ -86,7 +96,7 @@ class ObjectBrowser {
                 tabbedPane(constraints: CENTER) {
                     if (inspector.object?.class?.array) {
                         scrollPane(name: ' Array data ') {
-                            arrayTable = table {
+                            arrayTable = table(selectionMode: SINGLE_SELECTION) {
                                 tableModel(list: inspector.object.toList().withIndex()) {
                                     closureColumn(header: 'Index', read: { it[1] })
                                     closureColumn(header: 'Value', read: { it[0] })
@@ -105,7 +115,8 @@ class ObjectBrowser {
                                         if (selectedRow != -1) {
                                             def value = arrayTable.model.getValueAt(selectedRow, 2)
                                             if (value != null) {
-                                                ObjectBrowser.inspect(value)
+                                                closeFrameIfAltDoubleClick(e)
+                                                ObjectBrowser.inspect(value, path + "[${arrayTable.model.getValueAt(selectedRow, 0)}]")
                                             }
                                         }
                                     }
@@ -114,7 +125,7 @@ class ObjectBrowser {
                         }
                     } else if (inspector.object instanceof Collection) {
                         scrollPane(name: ' Collection data ') {
-                            collectionTable = table {
+                            collectionTable = table(selectionMode: SINGLE_SELECTION) {
                                 tableModel(list: inspector.object.withIndex()) {
                                     closureColumn(header: 'Index', read: { it[1] })
                                     closureColumn(header: 'Value', read: { it[0] })
@@ -133,7 +144,8 @@ class ObjectBrowser {
                                         if (selectedRow != -1) {
                                             def value = collectionTable.model.getValueAt(selectedRow, 2)
                                             if (value != null) {
-                                                ObjectBrowser.inspect(value)
+                                                closeFrameIfAltDoubleClick(e)
+                                                ObjectBrowser.inspect(value, path + "[${collectionTable.model.getValueAt(selectedRow, 0)}]")
                                             }
                                         }
                                     }
@@ -142,7 +154,7 @@ class ObjectBrowser {
                         }
                     } else if (inspector.object instanceof Map) {
                         scrollPane(name: ' Map data ') {
-                            mapTable = table {
+                            mapTable = table(selectionMode: SINGLE_SELECTION) {
                                 tableModel(list: inspector.object.entrySet().withIndex()) {
                                     closureColumn(header: 'Index', read: { it[1] })
                                     closureColumn(header: 'Key', read: { it[0].key })
@@ -162,7 +174,8 @@ class ObjectBrowser {
                                         if (selectedRow != -1) {
                                             def value = mapTable.model.getValueAt(selectedRow, 2)
                                             if (value != null) {
-                                                ObjectBrowser.inspect(value)
+                                                closeFrameIfAltDoubleClick(e)
+                                                ObjectBrowser.inspect(value, path + "[${mapTable.model.getValueAt(selectedRow, 1)}]")
                                             }
                                         }
                                     }
@@ -172,7 +185,7 @@ class ObjectBrowser {
                     }
                     scrollPane(name: ' Properties (includes public fields) ') {
                         def data = Inspector.sortWithRawValue(inspector.propertyInfoWithRawValue.toList())
-                        fieldTable = table {
+                        fieldTable = table(selectionMode: SINGLE_SELECTION) {
                             tableModel(list: data) {
                                 closureColumn(header: 'Name', read: { it[MEMBER_NAME_IDX] })
                                 closureColumn(header: 'Value', read: { it[MEMBER_VALUE_IDX] })
@@ -195,7 +208,8 @@ class ObjectBrowser {
                                     if (selectedRow != -1) {
                                         def value = fieldTable.model.getValueAt(selectedRow, MEMBER_RAW_VALUE_IDX)
                                         if (value != null) {
-                                            ObjectBrowser.inspect(value)
+                                            closeFrameIfAltDoubleClick(e)
+                                            ObjectBrowser.inspect(value, path + (path.length() === 0 ? '' : '.') + "${fieldTable.model.getValueAt(selectedRow, 0)}")
                                         }
                                     }
                                 }
@@ -203,7 +217,7 @@ class ObjectBrowser {
                         })
                     }
                     scrollPane(name: ' (Meta) Methods ') {
-                        methodTable = table {
+                        methodTable = table(selectionMode: SINGLE_SELECTION) {
                             def data = Inspector.sort(inspector.methods.toList())
                             data.addAll(Inspector.sort(inspector.metaMethods.toList()))
                             tableModel(list: data) {
@@ -218,6 +232,13 @@ class ObjectBrowser {
                         }
                     }
                 }
+                panel(name: 'Path',
+                        border: emptyBorder([5, 10, 5, 10]),
+                        constraints: SOUTH) {
+                    boxLayout(axis: 2)
+                    label('Path:  ')
+                    textField(editable: false, text: path)
+                }
             }
         }
 
@@ -239,6 +260,13 @@ class ObjectBrowser {
         }
     }
 
+    void closeFrameIfAltDoubleClick(MouseEvent e) {
+        if (e.altDown) {
+            frame.visible = false;
+            frame.dispose();
+        }
+    }
+
     void showAbout(EventObject evt) {
         def pane = swing.optionPane()
         // work around GROOVY-1048


[groovy] 02/03: GROOVY-10661: reduce some duplication in ObjectBrowser

Posted by pa...@apache.org.
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

commit bfb89e0ec03c69620bc6dd76ea2de7274786df31
Author: Paul King <pa...@asert.com.au>
AuthorDate: Thu Jun 23 16:29:15 2022 +1000

    GROOVY-10661: reduce some duplication in ObjectBrowser
---
 .../groovy/groovy/console/ui/ObjectBrowser.groovy  | 83 +++++++---------------
 1 file changed, 26 insertions(+), 57 deletions(-)

diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
index fcd4fda663..40c2630adf 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ObjectBrowser.groovy
@@ -57,12 +57,8 @@ class ObjectBrowser {
         inspect('some String')
     }
 
-    public ObjectBrowser(String path = '') {
-        this.path = (path == null ? '' : path)
-    }
-
     static void inspect(objectUnderInspection, String path = '') {
-        def browser = new ObjectBrowser(path)
+        def browser = new ObjectBrowser(path: path ?: '')
         browser.inspector = new Inspector(objectUnderInspection)
         if (objectUnderInspection != null && browser.path == '') {
             browser.path = "${objectUnderInspection.getClass().name} instance"
@@ -108,19 +104,8 @@ class ObjectBrowser {
                                 maxWidth = 0
                                 width = 0
                             }
-                            arrayTable.addMouseListener(new MouseAdapter() {
-                                void mouseClicked(MouseEvent e) {
-                                    if (e.clickCount == 2) {
-                                        def selectedRow = arrayTable.selectedRow
-                                        if (selectedRow != -1) {
-                                            def value = arrayTable.model.getValueAt(selectedRow, 2)
-                                            if (value != null) {
-                                                closeFrameIfAltDoubleClick(e)
-                                                ObjectBrowser.inspect(value, path + "[${arrayTable.model.getValueAt(selectedRow, 0)}]")
-                                            }
-                                        }
-                                    }
-                                }
+                            arrayTable.addMouseListener(makeClickAdapter(arrayTable, 2) { row ->
+                                path + "[${arrayTable.model.getValueAt(row, 0)}]"
                             })
                         }
                     } else if (inspector.object instanceof Collection) {
@@ -137,19 +122,8 @@ class ObjectBrowser {
                                 maxWidth = 0
                                 width = 0
                             }
-                            collectionTable.addMouseListener(new MouseAdapter() {
-                                void mouseClicked(MouseEvent e) {
-                                    if (e.clickCount == 2) {
-                                        def selectedRow = collectionTable.selectedRow
-                                        if (selectedRow != -1) {
-                                            def value = collectionTable.model.getValueAt(selectedRow, 2)
-                                            if (value != null) {
-                                                closeFrameIfAltDoubleClick(e)
-                                                ObjectBrowser.inspect(value, path + "[${collectionTable.model.getValueAt(selectedRow, 0)}]")
-                                            }
-                                        }
-                                    }
-                                }
+                            collectionTable.addMouseListener(makeClickAdapter(collectionTable, 2) { row ->
+                                path + "[${collectionTable.model.getValueAt(row, 0)}]"
                             })
                         }
                     } else if (inspector.object instanceof Map) {
@@ -167,19 +141,8 @@ class ObjectBrowser {
                                 maxWidth = 0
                                 width = 0
                             }
-                            mapTable.addMouseListener(new MouseAdapter() {
-                                void mouseClicked(MouseEvent e) {
-                                    if (e.clickCount == 2) {
-                                        def selectedRow = mapTable.selectedRow
-                                        if (selectedRow != -1) {
-                                            def value = mapTable.model.getValueAt(selectedRow, 2)
-                                            if (value != null) {
-                                                closeFrameIfAltDoubleClick(e)
-                                                ObjectBrowser.inspect(value, path + "[${mapTable.model.getValueAt(selectedRow, 1)}]")
-                                            }
-                                        }
-                                    }
-                                }
+                            mapTable.addMouseListener(makeClickAdapter(mapTable, 2) { row ->
+                                path + "[${mapTable.model.getValueAt(row, 1)}]"
                             })
                         }
                     }
@@ -201,19 +164,8 @@ class ObjectBrowser {
                             maxWidth = 0
                             width = 0
                         }
-                        fieldTable.addMouseListener(new MouseAdapter() {
-                            void mouseClicked(MouseEvent e) {
-                                if (e.clickCount == 2) {
-                                    def selectedRow = fieldTable.selectedRow
-                                    if (selectedRow != -1) {
-                                        def value = fieldTable.model.getValueAt(selectedRow, MEMBER_RAW_VALUE_IDX)
-                                        if (value != null) {
-                                            closeFrameIfAltDoubleClick(e)
-                                            ObjectBrowser.inspect(value, path + (path.length() === 0 ? '' : '.') + "${fieldTable.model.getValueAt(selectedRow, 0)}")
-                                        }
-                                    }
-                                }
-                            }
+                        fieldTable.addMouseListener(makeClickAdapter(fieldTable, MEMBER_RAW_VALUE_IDX) { row ->
+                            path + (path.size() == 0 ? '' : '.') + "${fieldTable.model.getValueAt(row, 0)}"
                         })
                     }
                     scrollPane(name: ' (Meta) Methods ') {
@@ -252,6 +204,23 @@ class ObjectBrowser {
         frame.toFront()
     }
 
+    def makeClickAdapter(table, int valueCol, Closure pathClosure) {
+        new MouseAdapter() {
+            void mouseClicked(MouseEvent e) {
+                if (e.clickCount == 2) {
+                    def selectedRow = table.selectedRow
+                    if (selectedRow != -1) {
+                        def value = table.model.getValueAt(selectedRow, valueCol)
+                        if (value != null) {
+                            closeFrameIfAltDoubleClick(e)
+                            ObjectBrowser.inspect(value, pathClosure(selectedRow))
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     void addSorter(table) {
         if (table != null) {
             def sorter = new TableSorter(table.model)