void run()

in subprojects/groovy-console/src/main/groovy/groovy/console/ui/AstBrowser.groovy [115:349]


    void run(Closure script, String name) {

        swing = new SwingBuilder()
        def phasePicker

        showScriptFreeForm = prefs.showScriptFreeForm
        showScriptClass = prefs.showScriptClass
        showClosureClasses = prefs.showClosureClasses
        showTreeView = prefs.showTreeView

        frame = swing.frame(title: 'Groovy AST Browser' + (name ? " - $name" : ''),
                location: prefs.frameLocation,
                size: prefs.frameSize,
                iconImage: swing.imageIcon(Console.ICON_PATH).image,
                defaultCloseOperation: WindowConstants.DISPOSE_ON_CLOSE,
                windowClosing: { event -> prefs.save(frame, splitterPane, mainSplitter, showScriptFreeForm, showScriptClass, showClosureClasses, phasePicker.selectedItem, showTreeView) }) {

            menuBar {
                menu(text: 'Show Script', mnemonic: 'S') {
                    checkBoxMenuItem(selected: showScriptFreeForm) {
                        action(name: 'Free Form', closure: this.&showScriptFreeForm,
                                mnemonic: 'F',)
                    }
                    checkBoxMenuItem(selected: showScriptClass) {
                        action(name: 'Class Form', closure: this.&showScriptClass,
                                mnemonic: 'C')
                    }
                    checkBoxMenuItem(selected: showClosureClasses) {
                        action(name: 'Generated Closure/Lambda Classes', closure: this.&showClosureClasses,
                                mnemonic: 'G')
                    }
                    checkBoxMenuItem(selected: showTreeView) {
                        action(name: 'Tree View', closure: this.&showTreeView,
                                mnemonic: 'T')
                    }
                }
                menu(text: 'View', mnemonic: 'V') {
                    menuItem {
                        action(
                                name: 'Larger Font',
                                closure: this.&largerFont,
                                mnemonic: 'L',
                                smallIcon: imageIcon(resource: 'icons/font_up.png', class: this),
                                accelerator: shortcut('shift L'))
                    }
                    menuItem {
                        action(name: 'Smaller Font',
                                closure: this.&smallerFont,
                                mnemonic: 'S',
                                smallIcon: imageIcon(resource: 'icons/font_down.png', class: this),
                                accelerator: shortcut('shift S'))
                    }
                    menuItem {
                        refreshAction = action(
                                name: 'Refresh',
                                closure: {
                                    decompile(phasePicker.selectedItem.phaseId, script())
                                    compile(jTree, script(), phasePicker.selectedItem.phaseId)
                                    initAuxViews()
                                },
                                mnemonic: 'R',
                                smallIcon: imageIcon(resource: 'icons/page_refresh.png', class: this),
                                accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0))
                    }
                }
                menu(text: 'Help', mnemonic: 'H') {
                    menuItem { action(
                            name: 'About',
                            closure: this.&aboutAction,
                            smallIcon: imageIcon(resource: 'icons/information.png', class: this),
                            mnemonic: 'A')
                    }
                }
            }
            panel {
                gridBagLayout()
                label(text: 'At end of Phase: ',
                        constraints: gbc(gridx: 0, gridy: 0, gridwidth: 1, gridheight: 1, weightx: 0, weighty: 0, anchor: WEST, fill: HORIZONTAL, insets: [2, 2, 2, 2]))
                phasePicker = comboBox(items: CompilePhaseAdapter.values(),
                        selectedItem: prefs.selectedPhase,
                        actionPerformed: {
                            // reset text to the default as the phase change removes the focus from the class node
                            initAuxViews()

                            decompile(phasePicker.selectedItem.phaseId, script())
                            compile(jTree, script(), phasePicker.selectedItem.phaseId)
                        },
                        constraints: gbc(gridx: 1, gridy: 0, gridwidth: 1, gridheight: 1, weightx: 1.0, weighty: 0, anchor: NORTHWEST, fill: NONE, insets: [2, 2, 2, 2]))
                button(text: 'Refresh',
                        actionPerformed: {
                            decompile(phasePicker.selectedItem.phaseId, script())
                            compile(jTree, script(), phasePicker.selectedItem.phaseId)
                            initAuxViews()
                        },
                        constraints: gbc(gridx: 2, gridy: 0, gridwidth: 1, gridheight: 1, weightx: 0, weighty: 0, anchor: NORTHEAST, fill: NONE, insets: [2, 2, 2, 3]))
                splitterPane = splitPane(
                        visible: showTreeView,
                        leftComponent: scrollPane {
                            jTree = tree(
                                    name: 'AstTreeView', rowHeight: 0, /* force recalc */
                                    model: new DefaultTreeModel(new DefaultMutableTreeNode('Loading...'))) {}
                        },
                        rightComponent: scrollPane {
                            propertyTable = table(new CellValueToolTipJTable(), selectionMode: SINGLE_SELECTION) {
                                tableModel(list: [[:]]) {
                                    propertyColumn(header: 'Name', propertyName: 'name')
                                    propertyColumn(header: 'Value  (double-click to browse)', propertyName: 'value')
                                    propertyColumn(header: 'Type', propertyName: 'type')
                                    propertyColumn(header: 'Raw', propertyName: 'raw')
                                }
                            }
                            propertyTable.columnModel.with {
                                // raw column hidden
                                getColumn(3).with {
                                    minWidth = 0
                                    maxWidth = 0
                                    width = 0
                                    preferredWidth = 0
                                }
                                // allow more space for value column
                                getColumn(0).preferredWidth = 100
                                getColumn(1).preferredWidth = 400
                                getColumn(2).preferredWidth = 100
                            }
                            propertyTable.addMouseListener(mouseListener(3) { row ->
                                'Browsing ' + jTree.lastSelectedPathComponent.userObject + ": " + propertyTable.model.getValueAt(row, 0)
                            })
                            propertyTable.setDefaultEditor(Object, null)
                        }
                ) {}
                mainSplitter = splitPane(
                        orientation: JSplitPane.VERTICAL_SPLIT,
                        topComponent: splitterPane,
                        bottomComponent: tabbedPane {
                            widget(decompiledSource = new ConsoleTextEditor(editable: false, showLineNumbers: false), title: 'Source')
                            widget(bytecodeView = new ConsoleTextEditor(editable: false, showLineNumbers: false), title: getByteCodeTitle())
                            widget(asmifierView = new ConsoleTextEditor(editable: false, showLineNumbers: false), title: getASMifierTitle())
                        },
                        constraints: gbc(gridx: 0, gridy: 2, gridwidth: 3, gridheight: 1, weightx: 1.0, weighty: 1.0, anchor: NORTHWEST, fill: BOTH, insets: [2, 2, 2, 2])) {
                }

            }
        }

        initAuxViews()

        propertyTable.model.rows.clear() //for some reason this suppress an empty row

        jTree.cellRenderer.setLeafIcon(swing.imageIcon(Console.NODE_ICON_PATH))
        jTree.selectionModel.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION
        jTree.addTreeSelectionListener({ TreeSelectionEvent e ->

            propertyTable.model.rows.clear()
            TreeNode node = jTree.lastSelectedPathComponent
            if (node instanceof TreeNodeWithProperties) {
                def titleSuffix = node.properties.find { list -> list[0] == 'text' }?.get(1)
                for (list in node.properties) {
                    propertyTable.model.rows << [name: list[0], value: list[1], type: list[2], raw: list[3]]
                }

                if (inputArea && rootElement) {
                    // get the line / column information to select the text represented by the current selected node
                    def lineInfo = node.properties.findAll {
                        it[0] in ['lineNumber', 'columnNumber', 'lastLineNumber', 'lastColumnNumber']
                    }
                    def lineInfoMap = lineInfo.inject([:]) { map, info -> map[(info[0])] = Integer.valueOf(info[1]); return map }

                    // when there are valid line / column information (ie. != -1), create a selection in the input area
                    if (!lineInfoMap.every { k, v -> v == -1 }) {
                        def startOffset = rootElement.getElement(lineInfoMap.lineNumber - 1).startOffset
                        inputArea.setCaretPosition(startOffset + lineInfoMap.columnNumber - 1)

                        def endOffset = rootElement.getElement(lineInfoMap.lastLineNumber - 1).startOffset
                        inputArea.moveCaretPosition(endOffset + lineInfoMap.lastColumnNumber - 1)
                    } else {
                        // if no line number is provided, unselect the current selection
                        // but keep the caret at the same position
                        inputArea.moveCaretPosition(inputArea.getCaretPosition())
                    }
                }

                if (node.classNode || node.methodNode) {
                    bytecodeView.textEditor.text = '// Loading bytecode ...'
                    asmifierView.textEditor.text = '// Loading ASMifier\'s output ...'
                    boolean showOnlyMethodCode = node.methodNode

                    swing.doOutside {
                        def className = showOnlyMethodCode ? node.getPropertyValue('declaringClass') : node.getPropertyValue('name')
                        def bytecode = classLoader.getBytecode(className)
                        if (bytecode) {
                            def methodName = node.getPropertyValue('name')
                            def methodDescriptor = node.getPropertyValue('descriptor')
                            boolean isMethodNameAndMethodDescriptorAvailable = methodName && methodDescriptor

                            String bytecodeSource = generateSource(bytecode, { writer -> new TraceClassVisitor(new PrintWriter(writer)) })
                            showSource(bytecodeView, bytecodeSource, showOnlyMethodCode, isMethodNameAndMethodDescriptorAvailable, {
                                "^.*\\n.*${Pattern.quote(methodName + methodDescriptor)}[\\s\\S]*?\\n[}|\\n]"
                            })

                            String asmifierSource = generateSource(bytecode, { writer -> new TraceClassVisitor(null, new ASMifier(), new PrintWriter(writer)) })
                            showSource(asmifierView, asmifierSource, showOnlyMethodCode, isMethodNameAndMethodDescriptorAvailable, {
                                "^.*\\n.*${Pattern.quote(methodName)}.*?${Pattern.quote(methodDescriptor)}[\\s\\S]*?\\n[}|\\n]"
                            })
                        } else {
                            swing.doLater {
                                bytecodeView.textEditor.text = NO_BYTECODE_AVAILABLE_AT_THIS_PHASE
                                asmifierView.textEditor.text = NO_BYTECODE_AVAILABLE_AT_THIS_PHASE
                            }
                        }
                    }

                } else {
                    bytecodeView.textEditor.text = ''
                    asmifierView.textEditor.text = ''
                }
            }
            propertyTable.model.fireTableDataChanged()
        } as TreeSelectionListener)

        updateFontSize(prefs.decompiledSourceFontSize)

        frame.pack()
        frame.location = prefs.frameLocation
        frame.size = prefs.frameSize
        splitterPane.dividerLocation = prefs.verticalDividerLocation
        mainSplitter.dividerLocation = prefs.horizontalDividerLocation
        frame.visible = true

        String source = script()
        decompile(phasePicker.selectedItem.phaseId, source)
        compile(jTree, source, phasePicker.selectedItem.phaseId)
        jTree.rootVisible = false
        jTree.showsRootHandles = true   // some OS's require this as a step to show nodes

    }