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
}