/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package groovy.swing

import javax.swing.JPopupMenu.Separator as JPopupMenu_Separator
import javax.swing.JToolBar.Separator as JToolBar_Separator

import java.awt.event.ActionEvent
import java.awt.event.InputEvent
import java.awt.event.KeyEvent
import java.text.SimpleDateFormat
import javax.swing.border.TitledBorder
import javax.swing.plaf.metal.MetalLookAndFeel
import javax.swing.text.DateFormatter
import javax.swing.text.NumberFormatter
import java.awt.*
import javax.swing.*

class SwingBuilderTest extends GroovySwingTestCase {

    void testWidgetId() {
        testInEDT {

            def swing = new SwingBuilder()
            def localVar = null

            swing.panel {
                label('byAttr', id: 'byAttr')
                byExpr = label('byExpr')
                localVar = label('localVar')
            }
            swing[SwingBuilder.DELEGATE_PROPERTY_OBJECT_ID] = 'key'
            swing.panel {
                label('byKey', key: 'byKey')
            }

            assert localVar != null
            assert swing.getVariables().containsKey('byAttr')
            assert swing.getVariables().containsKey('byExpr')
            assert !swing.getVariables().containsKey('localVar')
            assert swing.getVariables().containsKey('byKey')
        }
    }

    void testNamedWidgetCreation() {
        testInEDT {
            def topLevelWidgets = [
                    frame: [JFrame, true],
                    dialog: [JDialog, true],
                    window: [JWindow, false],
                    fileChooser: [JFileChooser, false],
                    optionPane: [JOptionPane, false]
            ]
            def swing = new SwingBuilder()
            topLevelWidgets.each { name, widgetInfo ->
                if (widgetInfo[1])
                    swing."$name"(id: "${name}Id".toString(), title: "This is my $name")
                else
                    swing."$name"(id: "${name}Id".toString())
                def widget = swing."${name}Id"
                assert widget.class == widgetInfo[0]
                if (widgetInfo[1]) assert widget.title == "This is my $name"
            }
        }
    }

    void testLayoutCreation() {
        testInEDT {

            def layouts = [
                    borderLayout: BorderLayout,
                    cardLayout: CardLayout,
                    flowLayout: FlowLayout,
                    gridBagLayout: GridBagLayout,
                    gridLayout: GridLayout,
                    springLayout: SpringLayout
            ]
            def swing = new SwingBuilder()
            layouts.each { name, expectedLayoutClass ->
                def frame = swing.frame {
                    "$name"()
                }
                assert frame.contentPane.layout.class == expectedLayoutClass
            }
        }
    }

    void testGridBagFactory() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.frame {
                gridBagLayout()
                label(fill: BOTH)
            }
            shouldFail {
                swing.frame {
                    flowLayout()
                    label(fill: BOTH)
                }
            }
            shouldFail {
                swing.frame {
                    label(fill: GridBagConstraints.BOTH)
                }
            }
        }
    }

    void testBorderLayout() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.frame {
                borderLayout()
                label("x", constraints: NORTH)
            }

            // test that BorderLayout.NORTH is not implied
            shouldFail(MissingPropertyException) {
                swing.frame {
                    label("x", constraints: NORTH)
                }
            }
        }
    }

    void testLayoutConstraintsProperty() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.frame {
                borderLayout(constraintsProperty: 'direction')
                label("x", direction: NORTH)
            }
        }
    }

    void testWidgetCreation() {
        testInEDT {

            def widgets = [
                    button: JButton,
                    checkBox: JCheckBox,
                    checkBoxMenuItem: JCheckBoxMenuItem,
                    colorChooser: JColorChooser,
                    comboBox: JComboBox,
                    desktopPane: JDesktopPane,
                    editorPane: JEditorPane,
                    formattedTextField: JFormattedTextField,
                    internalFrame: JInternalFrame,
                    label: JLabel,
                    layeredPane: JLayeredPane,
                    list: JList,
                    menu: JMenu,
                    menuBar: JMenuBar,
                    menuItem: JMenuItem,
                    panel: JPanel,
                    passwordField: JPasswordField,
                    popupMenu: JPopupMenu,
                    progressBar: JProgressBar,
                    radioButton: JRadioButton,
                    radioButtonMenuItem: JRadioButtonMenuItem,
                    scrollBar: JScrollBar,
                    scrollPane: JScrollPane,
                    separator: JSeparator,
                    slider: JSlider,
                    spinner: JSpinner,
                    splitPane: JSplitPane,
                    tabbedPane: JTabbedPane,
                    table: JTable,
                    textArea: JTextArea,
                    textPane: JTextPane,
                    textField: JTextField,
                    toggleButton: JToggleButton,
                    toolBar: JToolBar,
                    tree: JTree,
                    viewport: JViewport,
            ]
            def swing = new SwingBuilder()
            widgets.each { name, expectedLayoutClass ->
                def frame = swing.frame {
                    "$name"(id: "${name}Id".toString())
                }
                assert swing."${name}Id".class == expectedLayoutClass
            }
        }
    }

    void testButtonGroup() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.panel {
                buttonGroup(id: 'group1')
                buttonGroup(id: 'group2')
                checkBox(id: 'cb1a', buttonGroup: group1)
                checkBox(id: 'cb1b', buttonGroup: group1)
                checkBox(id: 'cb2a', buttonGroup: group2)
                checkBox(id: 'cb2b', buttonGroup: group2)

            }

            def statusCBs = {[swing.cb1a.selected, swing.cb1b.selected, swing.cb2a.selected, swing.cb2b.selected]}

            assert statusCBs() == [false, false, false, false]

            swing.cb1a.selected = true
            assert statusCBs() == [true, false, false, false]

            swing.cb1b.selected = true
            assert statusCBs() == [false, true, false, false]

            swing.cb2a.selected = true
            assert statusCBs() == [false, true, true, false]

            swing.cb2b.selected = true
            assert statusCBs() == [false, true, false, true]

        }
    }

    void testButtonGroupOnlyForButtons() {
        testInEDT {
            def swing = new SwingBuilder()

            def buttonGroup = swing.buttonGroup()
            shouldFail(MissingPropertyException) {
                swing.label(buttonGroup: buttonGroup)
            }
        }
    }

    void testWidget() {
        testInEDT {

            def swing = new SwingBuilder()
            def label = swing.label("By Value:")
            def widgetByValue = swing.widget(label)
            assert widgetByValue != null
            def widgetByLabel = swing.widget(widget: label)
            assert widgetByLabel != null
        }
    }

    void testSplitPane() {
        testInEDT {

            def swing = new SwingBuilder()
            def buttonGroup = swing.buttonGroup()
            def frame = swing.frame {
                splitPane(id: 'hsplit', orientation: JSplitPane.HORIZONTAL_SPLIT) {
                    button(id: 'left', buttonGroup: buttonGroup)
                    button(id: 'right', buttonGroup: buttonGroup)
                }
                splitPane(id: 'vsplit', orientation: JSplitPane.VERTICAL_SPLIT) {
                    button(id: 'top')
                    button(id: 'bottom')
                }
            }
            assert swing.hsplit.leftComponent == swing.left
            assert swing.hsplit.rightComponent == swing.right
            assert swing.vsplit.topComponent == swing.top
            assert swing.vsplit.bottomComponent == swing.bottom
        }
    }

    void testNestedWindows() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.window(id: 'root') {
                window(id: 'child1')
                frame(id: 'child2') {
                    window(id: 'child2_1')
                }
            }
            swing.window(id: 'root2')

            //assert swing.root.owner == null
            assert swing.child1.owner == swing.root
            assert swing.child2.owner == null // it's a frame, frames have no owners
            assert swing.child2_1.owner == swing.child2

            assert swing.root2.owner != swing.root
            assert swing.root2.owner != swing.child1
            assert swing.root2.owner != swing.child2
            assert swing.root2.owner != swing.child2_1

            swing.panel {
                swing.frame()
                swing.window()
                swing.dialog()
            }
        }
    }

    void testFrames() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.frame(id: 'frame') {
                button('test', id: 'button', defaultButton: true)
            }
            assert swing.frame.rootPane.defaultButton == swing.button
            assert swing.button.defaultButton
        }
    }

    void testDialogs() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.dialog(id: 'd1')
            swing.frame(id: 'f') { dialog(id: 'fd') }
            swing.dialog(id: 'd') { dialog(id: 'dd') }
            swing.dialog(id: 'd2')

            //assert swing.d1.owner == null
            assert swing.fd.owner == swing.f
            assert swing.dd.owner == swing.d

            assert swing.d2.owner != swing.dd
            assert swing.d2.owner != swing.fd
            assert swing.d2.owner != swing.d
            assert swing.d2.owner != swing.f
            assert swing.d2.owner != swing.d1
        }
    }

    void testWindows() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.window()
            swing.frame { window() }
            swing.dialog { window() }
        }
    }

    void testNodeCreation() {
        testInEDT {

            def swing = new SwingBuilder()
            def frame = swing.frame {
                // 4 valid parameter combinations
                button()
                button('Text')
                button(label: 'Label')
                button(label: 'Label', 'Text')
            }
            shouldFail {
                frame = swing.frame {
                    // invalid parameter
                    button(new Date())
                }
            }
        }
    }

    void testSetMnemonic() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.panel(layout: new BorderLayout()) {
                label(id: 'label0', text: 'Name0', displayedMnemonic: 48)
                label(id: 'label1', text: 'Name1', displayedMnemonic: 'N' as char)
            }
            int expected0 = '0'
            int expected1 = 'N'
            assert swing.label0.displayedMnemonic == expected0
            assert swing.label1.displayedMnemonic == expected1
            swing.menuBar {
                menu {
                    menuItem {
                        action(id: 'actionId', name: 'About', mnemonic: 'A')
                    }
                }
            }
            int expected2 = 'A'
            int actual = swing.actionId.getValue(Action.MNEMONIC_KEY)
            assert actual == expected2
        }
    }

    void testBuilderProperties() {
        testInEDT {

            def swing = new SwingBuilder()
            assert swing.class.name == SwingBuilder.class.name
        }
    }

    void testFormattedTextField() {
        testInEDT {

            def swing = new SwingBuilder()
            def dummy = new Date()
            def field = swing.formattedTextField(value: dummy)
            assert field.value == dummy
            assert field.formatter.class == DateFormatter
            def dummyFormatter = new SimpleDateFormat()
            field = swing.formattedTextField(format: dummyFormatter)
            assert field.formatter.class == DateFormatter
            field = swing.formattedTextField()
            field.value = 3
            assert field.formatter.class == NumberFormatter
        }
    }

    void testScrollPane() {
        testInEDT {

            def swing = new SwingBuilder()
            shouldFail {
                swing.scrollPane {
                    button("OK")
                    button("Cancel")
                }
            }
        }
    }

    void testComboBox() {
        testInEDT {

            def swing = new SwingBuilder()
            Object[] objects = ['a', 'b']
            def list = ['c', 'd', 'e']
            def vector = new Vector(['f', 'g', 'h', 'i'])
            assert swing.comboBox(items: objects).itemCount == 2
            assert swing.comboBox(items: list).itemCount == 3
            assert swing.comboBox(items: vector).itemCount == 4
            assert swing.comboBox().itemCount == 0
        }
    }

    void testList() {
        testInEDT {

            def swing = new SwingBuilder()
            Object[] objects = ['a', 'b']
            def list = ['c', 'd', 'e']
            def vector = new Vector(['f', 'g', 'h', 'i'])
            assert swing.list(items: objects).model.size == 2
            assert swing.list(items: list).model.size == 3
            assert swing.list(items: vector).model.size == 4
            assert swing.list().model.size == 0
            assert swing.list(listData: objects).model.size == 2
            assert swing.list(listData: list).model.size == 3
            assert swing.list(listData: vector).model.size == 4
            assert swing.list(listData: "list").model.size == 4
            assert swing.list(listData: [a: 1, b: 2].collect { k, v -> v}).model.size == 2
            def theList = swing.list(items: list)
            list[1] = 'a'
            assert theList.model.getElementAt(1) == 'a'
            theList.model.add('z')
            assert list.size() == 4
            assert list[3] == 'z'
        }
    }

    void testMisplacedActionsAreIgnored() {
        testInEDT {

            def swing = new SwingBuilder()
            // labels don't support actions; should be ignored
            swing.label {
                action(id: 'actionId', Name: 'about', mnemonic: 'A', closure: {x -> x})
                map()
            }
            swing.panel {
                borderLayout {
                    // layouts don't support actions, will be ignored
                    action(id: 'actionId')
                }
            }
        }
    }

    void testBoxLayout() {
        testInEDT {

            def swing = new SwingBuilder()
            def message = shouldFail {
                swing.boxLayout()
            }
            assert message.contains('Must be nested inside a Container')
            // default is X_AXIS
            swing.panel(id: 'panel') {
                boxLayout(id: 'layout1')
            }
            // can also set explicit axis
            swing.frame(id: 'frame') {
                boxLayout(id: 'layout2', axis: BoxLayout.Y_AXIS)
            }
        }
    }

    void testKeystrokesWithinActions() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.panel {
                button(id: 'buttonId') {
                    action(id: 'action1', keyStroke: 'ctrl W')
                    action(id: 'action2',
                            keyStroke: KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.ALT_MASK))
                    action(id: 'action3', keyStroke: "${'ctrl Z'}")
                }
            }
            def component = swing.buttonId
            def expected1 = swing.action1.toString()
            def expected2 = swing.action2.toString()
            def expected3 = swing.action3.toString()
            def keys = component.actionMap.allKeys().toList()
            assert keys.contains(expected1)
            assert keys.contains(expected2)
            assert keys.contains(expected3)
            def inputMap = component.inputMap
            def values = inputMap.allKeys().toList().collect { inputMap.get(it) }
            assert values.contains(expected1)
            assert values.contains(expected2)
            assert values.contains(expected3)
        }
    }

    void testActionClosures() {
        testInEDT {

            def swing = new SwingBuilder()
            def testTarget = 'blank'
            swing.actions {
                action(id: 'a', closure: {testTarget = 'A'})
                action(id: 'b') {testTarget = 'B' }
                action(id: 'c', closure: {evt -> testTarget = 'C'})
                action(id: 'd') {evt -> testTarget = 'D' }
            }

            ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")
            assert testTarget == 'blank'
            swing.a.actionPerformed(evt)
            assert testTarget == 'A'
            swing.b.actionPerformed(evt)
            assert testTarget == 'B'
            swing.c.actionPerformed(evt)
            assert testTarget == 'C'
            swing.d.actionPerformed(evt)
            assert testTarget == 'D'

            // negative tests
            swing.actions {
                action(id: 'z')
                shouldFail(RuntimeException) {
                    action(id: 'y', closure: {testTarget = 'Y'}) {testTarget = 'YY'}
                }
                shouldFail(RuntimeException) {
                    action([actionPerformed: {testTarget = 'X'}] as AbstractAction, id: 'x') { testTarget = 'XX'}
                }
            }
            shouldFail(NullPointerException) {
                swing.z.actionPerformed(evt)
            }

        }
    }

    void testSetAccelerator() {
        testInEDT {

            def swing = new SwingBuilder()
            def help = swing.action(accelerator: 'F1')
            def about = swing.action(accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK))
            assert help.getValue(Action.ACCELERATOR_KEY).toString()
                    .indexOf(KeyEvent.getKeyText(KeyEvent.VK_F1)) > -1
            def aboutKeyStroke = about.getValue(Action.ACCELERATOR_KEY)
            assert aboutKeyStroke.keyCode == KeyEvent.VK_SPACE
            assert (aboutKeyStroke.modifiers & InputEvent.CTRL_MASK) != 0
        }
    }

    private verifyAccel(action, int mustHave = 0) {
        int mods = action.getValue(Action.ACCELERATOR_KEY).modifiers
        assert mods != 0
        assert (mods & mustHave) == mustHave
        // don't assert (modd % musthave) != 0 because mustHave may be the platform shortcut modifer
        return action
    }

    void testSetAcceleratorShortcuts() {
        testInEDT {

            def swing = new SwingBuilder()
            char q = 'Q'
            swing.actions {
                verifyAccel(action(accelerator: shortcut(q)))
                verifyAccel(action(accelerator: shortcut(q, InputEvent.SHIFT_DOWN_MASK)), InputEvent.SHIFT_DOWN_MASK)
                verifyAccel(action(accelerator: shortcut(KeyEvent.VK_NUMPAD5)))
                verifyAccel(action(accelerator: shortcut(KeyEvent.VK_NUMPAD5, InputEvent.SHIFT_DOWN_MASK)), InputEvent.SHIFT_DOWN_MASK)
                verifyAccel(action(accelerator: shortcut('DELETE')))
                verifyAccel(action(accelerator: shortcut('DELETE', InputEvent.SHIFT_DOWN_MASK)), InputEvent.SHIFT_DOWN_MASK)
            }
        }
    }

    void testBorderLayoutConstraints() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.internalFrame(id: 'frameId',
                    border: BorderFactory.createTitledBorder(BorderFactory.createLoweredBevelBorder())) {
                swing.frameId.contentPane.layout = new BorderLayout()
                vbox(id: 'vboxId', constraints: BorderLayout.NORTH)
                hbox(id: 'hboxId', constraints: BorderLayout.WEST)
                rigidArea(id: 'area1', constraints: BorderLayout.EAST, size: [3, 4] as Dimension)
                rigidArea(id: 'area2', constraints: BorderLayout.SOUTH, width: 30, height: 40)
                scrollPane(id: 'scrollId', constraints: BorderLayout.CENTER,
                        border: BorderFactory.createRaisedBevelBorder()) {
                    panel() {
                        glue()
                        vglue()
                        hglue()
                        vstrut()
                        vstrut(height: 8)
                        hstrut()
                        hstrut(width: 8)
                        rigidArea(id: 'area3')
                    }
                }
            }
            assert swing.vboxId.parent == swing.frameId.contentPane
            assert swing.hboxId.parent == swing.frameId.contentPane
            assert swing.scrollId.parent == swing.frameId.contentPane
        }
    }

    void testSetConstraints() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.panel(layout: new BorderLayout()) {
                label(text: 'Name', constraints: BorderLayout.CENTER)
            }
        }
    }

    void testSetToolTipText() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.panel(layout: new BorderLayout()) {
                label(id: 'labelId', text: 'Name', toolTipText: 'This is the name field')
            }

        }
    }

    void testAttributeOrdering() {
        testInEDT {

            def swing = new SwingBuilder()

            def frame = swing.frame(
                    size: [500, 500],
                    locationRelativeTo: null
            )
            def locationFirst = frame.location

            frame = swing.frame(
                    locationRelativeTo: null,
                    size: [500, 500]
            )
            def locationLast = frame.location

            // setLocationReativeTo(null) places the component in the center of
            // the screen, relative to its size, so centering it after sizing it
            // should result in a 250,250 offset from centering it before sizing it
            assert locationFirst != locationLast
        }
    }

    void testWidgetPassthroughConstraints() {
        testInEDT {

            def swing = new SwingBuilder()
            def foo = swing.button('North')
            def frame = swing.frame {
                borderLayout()
                widget(foo, constraints: BorderLayout.NORTH)
                // a failed test throws MissingPropertyException by now
            }
        }
    }

    void testGROOVY1837ReuseAction() {
        testInEDT {

            def swing = new SwingBuilder()

            def testAction = swing.action(name: 'test', mnemonic: 'A', accelerator: 'ctrl R')
            assert testAction.getValue(Action.MNEMONIC_KEY) != null
            assert testAction.getValue(Action.ACCELERATOR_KEY) != null

            swing.action(testAction)
            assert testAction.getValue(Action.MNEMONIC_KEY) != null
            assert testAction.getValue(Action.ACCELERATOR_KEY) != null
        }
    }

    void testSeparators() {
        testInEDT {

            def swing = new SwingBuilder()
            swing.frame {
                menu("test") {
                    separator(id: "menuSep")
                }
                toolBar {
                    separator(id: "tbSep")
                }
                separator(id: "sep")
            }
            assert swing.menuSep instanceof JPopupMenu_Separator
            assert swing.tbSep instanceof JToolBar_Separator
            assert swing.sep instanceof JSeparator
        }
    }

    void testCollectionNodes() {
        testInEDT {

            def swing = new SwingBuilder()
            def collection = swing.actions {
                action(id: 'test')
            }
            assert collection.contains(swing.test)
        }
    }

    void testFactoryCornerCases() {
        testInEDT {

            def swing = new SwingBuilder()

            // change in 1.6, bad node names throw exceptions instead of being ignored
            shouldFail(MissingMethodException) {
                swing.bogusWidget() == null
            }

            swing.registerFactory("nullWidget",
                    [newInstance: {builder, name, value, props -> null}] as AbstractFactory)
            assert swing.nullWidget() == null
        }
    }

    void testFactoryLogging() {
        testInEDT {

            def logger = java.util.logging.Logger.getLogger(SwingBuilder.class.name)
            def oldLevel = logger.getLevel()
            logger.setLevel(java.util.logging.Level.FINE)
            def swing = new SwingBuilder()
            swing.label()
            logger.setLevel(oldLevel)
        }
    }

    void testEnhancedValueArguments() {
        testInEDT {

            def swing = new SwingBuilder()

            // elements that take an action, icon, string, GString,
            // or their own type as a value arg
            def anAction = swing.action(name: "test action")
            def icon = new javax.swing.plaf.metal.MetalComboBoxIcon()
            def richActionItems = [
                    'button',
                    'checkBox',
                    'radioButton',
                    'toggleButton',
                    'menuItem',
                    'checkBoxMenuItem',
                    'radioButtonMenuItem'
            ]

            richActionItems.each {name ->
                swing."$name"(anAction, id: "${name}Action".toString())
                swing."$name"(icon, id: "${name}Icon".toString())
                swing."$name"("string", id: "${name}String".toString())
                swing."$name"("${'g'}string", id: "${name}GString".toString())
                swing."$name"(swing."${name}Action", id: "${name}Self".toString())

                assert swing."${name}Action"
                assert swing."${name}Icon"
                assert swing."${name}String".text == 'string'
                assert swing."${name}GString".text == 'gstring'
                assert swing."${name}Self" == swing."${name}Action"
                shouldFail {
                    swing."$name"(['bad'])
                }
            }

            // elements that take no value argument
            def noValueItems = [
                    "actions",
                    "boxLayout",
                    "formattedTextField",
                    "glue",
                    "hbox",
                    "hglue",
                    "hstrut",
                    "map",
                    "rigidArea",
                    "separator",
                    "vbox",
                    "vglue",
                    "vstrut",
            ]

            noValueItems.each {name ->
                //println name
                shouldFail {
                    swing.frame {
                        "$name"(swing."$name"(), id: "${name}Self".toString())
                    }
                }
            }

            // elements that only take their own type as a value argument
            def selfItems = [
                    "action",
                    "borderLayout",
                    "boundedRangeModel",
                    "box",
                    "buttonGroup",
                    "cardLayout",
                    //"closureColumn",
                    "colorChooser",
                    "comboBox",
                    //"container",
                    "desktopPane",
                    "dialog",
                    "fileChooser",
                    "flowLayout",
                    "frame",
                    "gbc",
                    "gridBagConstraints",
                    "gridBagLayout",
                    "gridLayout",
                    "internalFrame",
                    "layeredPane",
                    // "list", // list acceps JList, Vector, Object[], List
                    "menuBar",
                    "optionPane",
                    //"overlayLayout",
                    "panel",
                    "popupMenu",
                    "progressBar",
                    //"propertyColumn",
                    "scrollBar",
                    "scrollPane",
                    "slider",
                    "spinner",
                    "spinnerDateModel",
                    "spinnerListModel",
                    "spinnerNumberModel",
                    "splitPane",
                    "springLayout",
                    "tabbedPane",
                    "table",
                    "tableColumn",
                    "tableLayout",
                    "tableModel",
                    //"td",
                    "toolBar",
                    //"tr",
                    "tree",
                    "viewport",
                    //"widget",
                    "window",
            ]
            selfItems.each {name ->
                //println name
                swing.frame {
                    "$name"(swing."$name"(), id: "${name}Self".toString())
                }

                shouldFail {
                    swing.frame {
                        swing."$name"(icon)
                    }
                }
            }

            // elements take their own type as a value argument or a [g]string as a text property
            def textItems = [
                    "editorPane",
                    "label",
                    "menu",
                    "passwordField",
                    "textArea",
                    "textField",
                    "textPane",
            ]
            textItems.each {name ->
                swing.frame {
                    "$name"(swing."$name"(), id: "${name}Self".toString())
                    "$name"('text', id: "${name}Text".toString())
                    "$name"("${'g'}string", id: "${name}GString".toString())
                }
                assert swing."${name}Text".text == 'text'
                assert swing."${name}GString".text == 'gstring'

                shouldFail {
                    swing.frame {
                        swing."$name"(icon)
                    }
                }
            }

            // leftovers...
            swing.frame {
                action(action: anAction)
                box(axis: BoxLayout.Y_AXIS)
                hstrut(5)
                vstrut(5)
                tableModel(tableModel: tableModel())
                container(id: 'c', panel()) {
                    widget(id: 'w', label("label"))
                    bean("anything")
                }
                container(container: panel()) {
                    widget(widget: label("label"))
                    bean(bean: "anything")
                }
            }
            assert swing.w.parent == swing.c
            shouldFail {
                swing.actions(property: 'fails')
            }
            shouldFail {
                swing.widget()
            }
            shouldFail {
                swing.widget(label('label')) {
                    label('No Content For You!')
                }
            }
            shouldFail {
                swing.container()
            }
            shouldFail {
                swing.bean()
            }
            shouldFail {
                swing.bean("anything") {
                    label('Nothing')
                }
            }
        }
    }

    boolean instancePass

    void markPassed() {
        instancePass = true
    }

    void testEDT() {
        if (HeadlessTestSupport.headless) return
        def swing = new SwingBuilder()

        boolean pass = false
        swing.edt { pass = SwingUtilities.isEventDispatchThread() }
        assert pass

        pass = false
        swing.edt { swing.edt { pass = SwingUtilities.isEventDispatchThread() } }
        assert pass

        instancePass = false
        swing.edt this.&markPassed
        assert instancePass
    }

    void testDoLater() {
        if (HeadlessTestSupport.headless) return
        def swing = new SwingBuilder()

        boolean pass = false
        swing.doLater {sleep 100; pass = true }
        assert !pass
        // check for pass changing up to 3 times, then call it a failed test
        int maxFailures = 3
        while (maxFailures > 0) {
            sleep 200
            if (pass) break
            maxFailures--
        }
        assert pass

        // doLater in the EDT is still a do later
        pass = false
        swing.edt { swing.doLater {sleep 100; pass = true } }
        assert !pass
        // check for pass changing up to 3 times, then call it a failed test
        maxFailures = 3
        while (maxFailures > 0) {
            sleep 200
            if (pass) break
            maxFailures--
        }
        assert pass

        instancePass = false
        swing.doLater this.&markPassed
        // check for pass changing up to 3 times, then call it a failed test
        maxFailures = 3
        while (maxFailures > 0) {
            sleep 50
            if (instancePass) break
            maxFailures--
        }
        assert instancePass
    }

    void testDoOutside() {
        testInEDT {
            def swing = new SwingBuilder()

            boolean pass = false
            swing.doOutside {sleep 100; pass = true }
            assert !pass
            // check for pass changing up to 3 times, then call it a failed test
            int maxFailures = 3
            while (maxFailures > 0) {
                sleep 200
                if (pass) break
                maxFailures--
            }
            assert pass

            pass = false
            swing.edt {
                swing.doOutside {sleep 100; pass = true }
                assert !pass
                // check for pass changing up to 3 times, then call it a failed test
                int myMaxFailures = 3
                while (myMaxFailures > 0) {
                    sleep 200
                    if (pass) break
                    myMaxFailures--
                }
                assert pass
            }

            instancePass = false
            swing.doOutside this.&markPassed
            // check for pass changing up to 3 times, then call it a failed test
            maxFailures = 3
            while (maxFailures > 0) {
                sleep 50
                if (instancePass) break
                maxFailures--
            }
            assert instancePass
        }
    }

    void testJumbledThreading() {
        if (HeadlessTestSupport.headless) return;

        def swing = new SwingBuilder()
        Closure threadTest = {c ->
            boolean notifyReached = false;
            Throwable caughtThrowable = null
            Thread t = Thread.start {
                try {
                    c()
                } catch (Throwable throwable) {
                    caughtThrowable = throwable
                }
                notifyReached = true
                synchronized (swing) { swing.notifyAll() }
            }

            synchronized (swing) { swing.wait(2000); }
            if (!notifyReached && t.isAlive()) {
                Thread.start {
                    sleep(1000)
                    exit(0)
                }
                fail("EDT Deadlock")
            }
            if (caughtThrowable) {
                throw caughtThrowable
            }
            assert swing.l.parent != null
            notifyReached = false
        }

        threadTest {
            swing.frame {
                edt {
                    label('label', id: 'l')
                }
            }
        }

        threadTest {
            swing.edt {
                swing.frame {
                    edt {
                        label('label', id: 'l')
                    }
                }
            }
        }

        // full build in EDT shold be fine
        threadTest {
            swing.edt {
                swing.frame {
                    label('label', id: 'l')
                }
            }
        }

        // nested build(Closure) call.
        // Bad form, but it shouldn't break stuff
        threadTest {
            swing.frame {
                build {
                    label('label', id: 'l')
                }
            }
        }

        // insure the legacy static build(Closure) call still works.
        def oldSwing = swing
        threadTest {
            swing = SwingBuilder.build {
                frame {
                    label('label', id: 'l')
                }
            }
        }
        assert swing != oldSwing
    }

    void testParallelBuild() {
        if (HeadlessTestSupport.headless) return;

        def swing = new SwingBuilder()
        def p
        def l

        Thread t1 = Thread.start {
            p = swing.panel() {
                sleep(100)
                label('child')
            }
        }
        Thread t2 = Thread.start {
            sleep(50)
            l = swing.label('loner')
        }

        t1.join()
        t2.join()

        assert l.parent == null
    }

    void testDispose() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.frame(id: 'frame').pack()
            swing.dialog(id: 'dialog').pack()
            swing.window(id: 'window').pack()

            //TODO check bind and model
            assert swing.frame.isDisplayable()
            assert swing.dialog.isDisplayable()
            assert swing.window.isDisplayable()
            swing.dispose()

            //TODO check bind and model
            assert !swing.frame.isDisplayable()
            assert !swing.dialog.isDisplayable()
            assert !swing.window.isDisplayable()

        }
    }

    void testPackAndShow() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.frame(id: 'frame', pack: true)
            swing.dialog(id: 'dialog', pack: true)
            swing.window(id: 'window', pack: true)

            assert swing.frame.isDisplayable()
            assert swing.dialog.isDisplayable()
            assert swing.window.isDisplayable()
            swing.dispose()

            swing.frame(id: 'frame', show: true)
            swing.dialog(id: 'dialog', show: true)
            swing.window(id: 'window', show: true)

            assert swing.frame.visible
            assert swing.dialog.visible
            assert swing.window.visible
            swing.dispose()

            swing.frame(id: 'frame', pack: true, show: true)
            swing.dialog(id: 'dialog', pack: true, show: true)
            swing.window(id: 'window', pack: true, show: true)

            assert swing.frame.visible
            assert swing.dialog.visible
            assert swing.window.visible
            swing.dispose()
        }
    }

    void testContainment() {
        testInEDT {
            def swing = new SwingBuilder()

            def topLevel = [
                    "window",
                    "frame",
                    "dialog",
                    "internalFrame",
            ]

            def containers = [
                    "hbox",
                    "box",
                    "desktopPane",
                    "layeredPane",
                    "panel",
                    "popupMenu",
                    //"scrollPane",
                    "splitPane",
                    "tabbedPane",
                    "toolBar",
                    "viewport",
            ]

            def components = [
                    "comboBox",
                    "formattedTextField",
                    "glue",
                    "hbox",
                    "hglue",
                    "hstrut",
                    "rigidArea",
                    "separator",
                    "vbox",
                    "vglue",
                    "vstrut",
                    "box",
                    "colorChooser",
                    "desktopPane",
                    "fileChooser",
                    "internalFrame",
                    "layeredPane",
                    "list",
                    "menu",
                    //"menuBar",
                    "optionPane",
                    "panel",
                    //"popupMenu",
                    "progressBar",
                    "scrollBar",
                    "scrollPane",
                    "slider",
                    "spinner",
                    "splitPane",
                    "tabbedPane",
                    "table",
                    "toolBar",
                    "tree",
                    "viewport",
                    "editorPane",
                    "label",
                    "passwordField",
                    "textArea",
                    "textField",
                    "textPane",
            ]


            topLevel.each {parentWidget ->
                components.each { childWidget ->
                    //println "$parentWidget / $childWidget"
                    def child
                    def parent = swing."$parentWidget" { child = "$childWidget"() }
                    assert parent.contentPane == child.parent
                }
            }

            containers.each {parentWidget ->
                components.each { childWidget ->
                    //println "$parentWidget / $childWidget"
                    def child
                    def parent = swing."$parentWidget" { child = "$childWidget"() }
                    assert parent == child.parent
                }
            }

            components.each { childWidget ->
                //println "scrollPane / $childWidget"

                def child
                def parent = swing.scrollPane { child = "$childWidget"() }
                if (childWidget == 'viewport') {
                    assert parent.viewport == child
                } else {
                    assert parent.viewport == child.parent
                }
            }
        }
    }

    void testMenus() {
        testInEDT {
            def swing = new SwingBuilder()

            def frame = swing.frame {
                menuBar(id: 'bar') {
                    menu('menu', id: 'menu') {
                        menuItem('item', id: 'item')
                        checkBoxMenuItem('check', id: 'check')
                        radioButtonMenuItem('radio', id: 'radio')
                        separator(id: 'sep')
                        menu('subMenu', id: 'subMenu') {
                            menuItem('item', id: 'subitem')
                            checkBoxMenuItem('check', id: 'subcheck')
                            radioButtonMenuItem('radio', id: 'subradio')
                            separator(id: 'subsep')
                            menu('subSubMenu', id: 'subSubMenu')
                        }
                    }
                }
            }

            assert frame.JMenuBar == swing.bar
            assert swing.bar.menuCount == 1
            assert swing.bar.getMenu(0) == swing.menu

            assert swing.menu.itemCount == 5
            assert swing.menu.getItem(0) == swing.item
            assert swing.menu.getItem(1) == swing.check
            assert swing.menu.getItem(2) == swing.radio
            assert swing.menu.getItem(3) == null // not a menu item
            assert swing.menu.getMenuComponent(3) == swing.sep
            assert swing.menu.getItem(4) == swing.subMenu
            shouldFail { swing.menu.getItem(5) }

            assert swing.subMenu.itemCount == 5
            assert swing.subMenu.getItem(0) == swing.subitem
            assert swing.subMenu.getItem(1) == swing.subcheck
            assert swing.subMenu.getItem(2) == swing.subradio
            assert swing.subMenu.getItem(3) == null // not a menu item
            assert swing.subMenu.getMenuComponent(3) == swing.subsep
            assert swing.subMenu.getItem(4) == swing.subSubMenu
        }
    }

    void testLookAndFeel() {
        testInEDT {
            def swing = new SwingBuilder()

            def oldLAF = UIManager.getLookAndFeel()
            try {
                // test LAFs guaranteed to be everywhere
                swing.lookAndFeel('metal')
                swing.lookAndFeel('system')
                swing.lookAndFeel('crossPlatform')

                // test alternate invocations...
                swing.lookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel())
                shouldFail {
                    swing.lookAndFeel(this)
                }
                swing.lookAndFeel('javax.swing.plaf.metal.MetalLookAndFeel')
                swing.lookAndFeel("${'j'}avax.swing.plaf.metal.MetalLookAndFeel")
                shouldFail {
                    swing.lookAndFeel('BogusLookAndFeel')
                }

                // test Metal Themeing and aux attributes
                swing.lookAndFeel('metal', theme: 'steel', boldFonts: false)
                shouldFail {
                    swing.lookAndFeel('metal', theme: 'steel', boldFonts: false, bogusAttribute: 'bad bad bad')
                }

                // test setup via attribute alone
                swing.lookAndFeel(lookAndFeel: 'metal', theme: 'steel', boldFonts: false)

                // test Look and Feel Closure
                swing.lookAndFeel('metal') { laf ->
                    assert laf instanceof MetalLookAndFeel
                }
                swing.lookAndFeel('metal', boldFonts: true) { laf ->
                    assert laf instanceof MetalLookAndFeel
                }
                swing.lookAndFeel(null, lookAndFeel: 'metal', boldFonts: true) { laf ->
                    assert laf instanceof MetalLookAndFeel
                }
                swing.lookAndFeel() { laf ->
                    assert laf != null
                }
            } finally {
                UIManager.setLookAndFeel(oldLAF)
            }
        }
    }

    void testMultiLookAndFeel() {
        testInEDT {
            def swing = new SwingBuilder()

            def oldLAF = UIManager.getLookAndFeel()
            try {
                def mlaf = new MetalLookAndFeel()
                assert swing.lookAndFeel('metal', 'bogus') instanceof MetalLookAndFeel
                assert swing.lookAndFeel('bogus', 'metal') instanceof MetalLookAndFeel
                assert swing.lookAndFeel(['metal'], 'bogus') instanceof MetalLookAndFeel
                assert swing.lookAndFeel('bogus', ['metal']) instanceof MetalLookAndFeel
                assert swing.lookAndFeel(['metal', [boldFonts: false]], 'bogus') instanceof MetalLookAndFeel
                assert swing.lookAndFeel('bogus', ['metal', [boldFonts: false]]) instanceof MetalLookAndFeel
                assert swing.lookAndFeel(mlaf, 'bogus') instanceof MetalLookAndFeel
                assert swing.lookAndFeel('bogus', mlaf) instanceof MetalLookAndFeel
                assert swing.lookAndFeel([mlaf], 'bogus') instanceof MetalLookAndFeel
                assert swing.lookAndFeel('bogus', [mlaf]) instanceof MetalLookAndFeel
                assert swing.lookAndFeel([mlaf, [boldFonts: false]], 'bogus') instanceof MetalLookAndFeel
                assert swing.lookAndFeel('bogus', [mlaf, [boldFonts: false]]) instanceof MetalLookAndFeel
                assert swing.lookAndFeel('bogus', 'fake', 'impossible') == null
            } finally {
                UIManager.setLookAndFeel(oldLAF)
            }
        }
    }

    void testBorders() {
        testInEDT {
            def swing = new SwingBuilder()

            // classic smoke test, try every valid combination and look for smoke...
            swing.frame {
                lineBorder(color: Color.BLACK, parent: true)
                lineBorder(color: Color.BLACK, thickness: 4, parent: true)
                lineBorder(color: Color.BLACK, roundedCorners: true, parent: true)
                lineBorder(color: Color.BLACK, thickness: 4, roundedCorners: true, parent: true)
                raisedBevelBorder(parent: true)
                raisedBevelBorder(highlight: Color.GREEN, shadow: Color.PINK, parent: true)
                raisedBevelBorder(highlightOuter: Color.GREEN, highlightInner: Color.RED, shadowOuter: Color.PINK, shadowInner: Color.BLUE, parent: true)
                loweredBevelBorder(parent: true)
                loweredBevelBorder(highlight: Color.GREEN, shadow: Color.PINK, parent: true)
                loweredBevelBorder(highlightOuter: Color.GREEN, highlightInner: Color.RED, shadowOuter: Color.PINK, shadowInner: Color.BLUE, parent: true)
                etchedBorder(parent: true)
                etchedBorder(highlight: Color.GREEN, shadow: Color.PINK, parent: true)
                loweredEtchedBorder(parent: true)
                loweredEtchedBorder(highlight: Color.GREEN, shadow: Color.PINK, parent: true)
                raisedEtchedBorder(parent: true)
                raisedEtchedBorder(highlight: Color.GREEN, shadow: Color.PINK, parent: true)
                titledBorder("Title 1", parent: true)
                titledBorder(title: "Title 2", parent: true)
                titledBorder("Title 3", position: 'bottom', parent: true)
                titledBorder(title: "Title 4", position: 'aboveBottom', parent: true)
                titledBorder("Title 5", position: TitledBorder.ABOVE_TOP, parent: true)
                titledBorder(title: "Title 6", position: TitledBorder.BOTTOM, parent: true)
                titledBorder("Title 7", justification: 'right', parent: true)
                titledBorder(title: "Title 8", justification: 'acenter', parent: true)
                titledBorder("Title 9", justification: TitledBorder.TRAILING, parent: true)
                titledBorder(title: "Title A", justification: TitledBorder.LEADING, parent: true)
                titledBorder("Title B", border: lineBorder(color: Color.RED, thickness: 6), parent: true)
                titledBorder(title: "Title C", border: lineBorder(color: Color.BLUE, thickness: 6), parent: true)
                titledBorder("Title D", color: Color.CYAN, parent: true)
                titledBorder(title: "Title E", border: lineBorder(color: Color.BLUE, thickness: 6), parent: true)
                emptyBorder(6, parent: true)
                emptyBorder([3, 5, 6, 9], parent: true)
                emptyBorder(top: 6, left: 5, bottom: 6, right: 9, parent: true)
                compoundBorder([titledBorder("single")], parent: true)
                compoundBorder([titledBorder("outer"), titledBorder("inner")], parent: true)
                compoundBorder(outer: titledBorder("outer"), inner: titledBorder("inner"), parent: true)
                compoundBorder([titledBorder("outer"), titledBorder("middle"), titledBorder("inner")], parent: true)
                matteBorder(Color.MAGENTA, size: 7, parent: true)
                matteBorder(7, color: Color.MAGENTA, parent: true)
                matteBorder(javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon(), size: 9, parent: true)
                matteBorder(9, icon: javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon(), parent: true)

                lineBorder(color: Color.BLACK)
                lineBorder(color: Color.BLACK, thickness: 4)
                lineBorder(color: Color.BLACK, roundedCorners: true)
                lineBorder(color: Color.BLACK, thickness: 4, roundedCorners: true)
                raisedBevelBorder()
                raisedBevelBorder(highlight: Color.GREEN, shadow: Color.PINK)
                raisedBevelBorder(highlightOuter: Color.GREEN, highlightInner: Color.RED, shadowOuter: Color.PINK, shadowInner: Color.BLUE)
                loweredBevelBorder()
                loweredBevelBorder(highlight: Color.GREEN, shadow: Color.PINK)
                loweredBevelBorder(highlightOuter: Color.GREEN, highlightInner: Color.RED, shadowOuter: Color.PINK, shadowInner: Color.BLUE)
                etchedBorder()
                etchedBorder(highlight: Color.GREEN, shadow: Color.PINK)
                loweredEtchedBorder()
                loweredEtchedBorder(highlight: Color.GREEN, shadow: Color.PINK)
                raisedEtchedBorder()
                raisedEtchedBorder(highlight: Color.GREEN, shadow: Color.PINK)
                titledBorder("Title 1")
                titledBorder(title: "Title 2")
                titledBorder("Title 3", position: 'bottom')
                titledBorder(title: "Title 4", position: 'aboveBottom')
                titledBorder("Title 5", position: TitledBorder.ABOVE_TOP)
                titledBorder(title: "Title 6", position: TitledBorder.BOTTOM)
                titledBorder("Title 7", justification: 'right')
                titledBorder(title: "Title 8", justification: 'acenter')
                titledBorder("Title 9", justification: TitledBorder.TRAILING)
                titledBorder(title: "Title A", justification: TitledBorder.LEADING)
                titledBorder("Title B", border: lineBorder(color: Color.RED, thickness: 6))
                titledBorder(title: "Title C", border: lineBorder(color: Color.BLUE, thickness: 6))
                titledBorder("Title D", color: Color.CYAN)
                titledBorder(title: "Title E", border: lineBorder(color: Color.BLUE, thickness: 6))
                emptyBorder(6)
                emptyBorder([3, 5, 6, 9])
                emptyBorder(top: 6, left: 5, bottom: 6, right: 9)
                compoundBorder([titledBorder("single")])
                compoundBorder([titledBorder("outer"), titledBorder("inner")])
                compoundBorder(outer: titledBorder("outer"), inner: titledBorder("inner"))
                compoundBorder([titledBorder("outer"), titledBorder("middle"), titledBorder("inner")])
                matteBorder(Color.MAGENTA, size: 7)
                matteBorder(7, color: Color.MAGENTA)
                matteBorder(javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon(), size: 9)
                matteBorder(9, icon: javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon())
            }
        }
    }

    void testBorderAttachment() {
        testInEDT {
            def swing = new SwingBuilder()

            swing.frame(id: 'frame') {
                raisedBevelBorder()
            }
            assert swing.frame.contentPane.border == null

            swing.frame(id: 'frame') {
                raisedBevelBorder(parent: true)
            }
            assert swing.frame.contentPane.border != null

            swing.panel(id: 'panel') {
                raisedBevelBorder()
            }
            assert swing.panel.border == null

            swing.panel(id: 'panel') {
                raisedBevelBorder(parent: true)
            }
            assert swing.panel.border != null
        }
    }

    void testRenderer() {
        testInEDT {
            def swing = new SwingBuilder()

            int count = 0
            def lcr = swing.listCellRenderer {
                label()
                onRender {
                    count++
                }
            }

            def f = swing.frame(pack: true, show: true) {
                ls = list(items: ["one", "two"], cellRenderer: lcr)
            }
            assert count == 2
            f.dispose()

            count = 0
            def lcr2 = swing.listCellRenderer {
                label()
                button()
                onRender {
                    count++
                    return (children[row % 2])
                }
            }

            assert lcr2.getListCellRendererComponent(swing.ls, "x", 0, false, false) instanceof javax.swing.JLabel
            assert lcr2.getListCellRendererComponent(swing.ls, "x", 1, false, false) instanceof javax.swing.JButton

        }
    }

    void testNoParent() {
        testInEDT {
            def swing = new SwingBuilder()
            def panel = swing.panel {
                button(id: "b1")
                noparent {
                    button(id: "b2")
                }
            }
            assert panel.componentCount == 1
            assert swing.b1.parent == panel
            assert !swing.b2.parent
        }
    }

    void testClientProperties() {
        testInEDT {
            def swing = new SwingBuilder()
            def button = swing.button(clientPropertyPropA: "A")
            assert button.getClientProperty("PropA") == "A"

            button = swing.button("clientPropertyPropWith.dotAnd:colon": ".&:")
            assert button.getClientProperty("PropWith.dotAnd:colon") == ".&:"

            button = swing.button(clientProperties: [prop1: "1", prop2: "2"])
            assert button.getClientProperty("prop1") == "1"
            assert button.getClientProperty("prop2") == "2"
        }
    }

    void testKeyStrokeAction() {
        testInEDT {
            def swing = new SwingBuilder()

            def noop = swing.action(name: "KeyAction", closure: {})

            // component as value
            def button = swing.button()
            swing.keyStrokeAction(button,
                    actionKey: "asValue",
                    keyStroke: "V",
                    action: noop)
            assert button.getInputMap(JComponent.WHEN_FOCUSED).get(KeyStroke.getKeyStroke("V")) == "asValue"
            assert button.actionMap.get("asValue") == noop

            // component as property
            swing.keyStrokeAction(component: button,
                    actionKey: "asProperty",
                    keyStroke: "P",
                    action: noop)
            assert button.getInputMap(JComponent.WHEN_FOCUSED).get(KeyStroke.getKeyStroke("P")) == "asProperty"
            assert button.actionMap.get("asProperty") == noop

            // nested in component
            button = swing.button {
                keyStrokeAction(actionKey: "nested",
                        keyStroke: "N",
                        action: noop)
            }
            assert button.getInputMap(JComponent.WHEN_FOCUSED).get(KeyStroke.getKeyStroke("N")) == "nested"
            assert button.actionMap.get("nested") == noop

            // nested in component + shortcut
            button = swing.button {
                keyStrokeAction(actionKey: "nested_shortcut",
                        keyStroke: shortcut("N"),
                        action: noop)
            }
            assert button.getInputMap(JComponent.WHEN_FOCUSED).get(swing.shortcut("N")) == "nested_shortcut"
            assert button.actionMap.get("nested_shortcut") == noop

            // kstroke as GString
            swing.keyStrokeAction(component: button,
                    actionKey: "GringKeyStroke",
                    keyStroke: "G",
                    action: noop)
            assert button.getInputMap(JComponent.WHEN_FOCUSED).get(KeyStroke.getKeyStroke("G")) == "GringKeyStroke"
            assert button.actionMap.get("GringKeyStroke") == noop

        }
    }

    void testAutomaticNameBasedOnIdAttribute() {
        testInEDT {
            def swing = new SwingBuilder()

            def node = swing.button(id: 'groovy')
            assert node == swing.groovy
            assert node.name == 'groovy'

            node = swing.button(id: 'groovy', name: 'java')
            assert node == swing.groovy
            assert node.name == 'java'

            node = swing.map(id: 'aMap')
            assert node == swing.aMap
            assert !node.name
        }
    }
}
