View Javadoc

1   /*
2    $Id: SwingBuilder.java,v 1.17 2005/10/25 12:32:11 alang Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package groovy.swing;
47  
48  import groovy.lang.Closure;
49  import groovy.lang.MissingMethodException;
50  
51  import groovy.model.DefaultTableModel;
52  import groovy.model.ValueHolder;
53  import groovy.model.ValueModel;
54  
55  import groovy.swing.impl.ComponentFacade;
56  import groovy.swing.impl.ContainerFacade;
57  import groovy.swing.impl.DefaultAction;
58  import groovy.swing.impl.Factory;
59  import groovy.swing.impl.Startable;
60  import groovy.swing.impl.TableLayout;
61  import groovy.swing.impl.TableLayoutCell;
62  import groovy.swing.impl.TableLayoutRow;
63  
64  import groovy.util.BuilderSupport;
65  
66  import java.awt.BorderLayout;
67  import java.awt.CardLayout;
68  import java.awt.Component;
69  import java.awt.Container;
70  import java.awt.Dimension;
71  import java.awt.Dialog;
72  import java.awt.FlowLayout;
73  import java.awt.Frame;
74  import java.awt.GridBagConstraints;
75  import java.awt.GridBagLayout;
76  import java.awt.GridLayout;
77  import java.awt.LayoutManager;
78  import java.awt.Window;
79  
80  import java.text.Format;
81  
82  import java.util.ArrayList;
83  import java.util.Collections;
84  import java.util.HashMap;
85  import java.util.Iterator;
86  import java.util.LinkedList;
87  import java.util.List;
88  import java.util.Map;
89  import java.util.Vector;
90  import java.util.logging.Level;
91  import java.util.logging.Logger;
92  
93  import javax.swing.AbstractButton;
94  import javax.swing.Action;
95  import javax.swing.Box;
96  import javax.swing.BoxLayout;
97  import javax.swing.ButtonGroup;
98  import javax.swing.DefaultBoundedRangeModel;
99  import javax.swing.JButton;
100 import javax.swing.JCheckBox;
101 import javax.swing.JCheckBoxMenuItem;
102 import javax.swing.JColorChooser;
103 import javax.swing.JComboBox;
104 import javax.swing.JComponent;
105 import javax.swing.JDesktopPane;
106 import javax.swing.JDialog;
107 import javax.swing.JEditorPane;
108 import javax.swing.JFileChooser;
109 import javax.swing.JFormattedTextField;
110 import javax.swing.JFrame;
111 import javax.swing.JInternalFrame;
112 import javax.swing.JLabel;
113 import javax.swing.JLayeredPane;
114 import javax.swing.JList;
115 import javax.swing.JMenu;
116 import javax.swing.JMenuBar;
117 import javax.swing.JMenuItem;
118 import javax.swing.JOptionPane;
119 import javax.swing.JPanel;
120 import javax.swing.JPasswordField;
121 import javax.swing.JPopupMenu;
122 import javax.swing.JProgressBar;
123 import javax.swing.JRadioButton;
124 import javax.swing.JRadioButtonMenuItem;
125 import javax.swing.JScrollBar;
126 import javax.swing.JScrollPane;
127 import javax.swing.JSeparator;
128 import javax.swing.JSlider;
129 import javax.swing.JSpinner;
130 import javax.swing.JSplitPane;
131 import javax.swing.JTabbedPane;
132 import javax.swing.JTable;
133 import javax.swing.JTextArea;
134 import javax.swing.JTextField;
135 import javax.swing.JTextPane;
136 import javax.swing.JToggleButton;
137 import javax.swing.JToolBar;
138 import javax.swing.JToolTip;
139 import javax.swing.JTree;
140 import javax.swing.JViewport;
141 import javax.swing.JWindow;
142 import javax.swing.KeyStroke;
143 import javax.swing.OverlayLayout;
144 import javax.swing.RootPaneContainer;
145 import javax.swing.SpinnerDateModel;
146 import javax.swing.SpinnerListModel;
147 import javax.swing.SpinnerNumberModel;
148 import javax.swing.SpringLayout;
149 import javax.swing.table.TableColumn;
150 import javax.swing.table.TableModel;
151 
152 import org.codehaus.groovy.runtime.InvokerHelper;
153 
154 /***
155  * A helper class for creating Swing widgets using GroovyMarkup
156  * 
157  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
158  * @version $Revision: 1.17 $
159  */
160 public class SwingBuilder extends BuilderSupport {
161 
162     private Logger log = Logger.getLogger(getClass().getName());
163     private Map factories = new HashMap();
164     private Object constraints;
165     private Map passThroughNodes = new HashMap();
166     private Map widgets = new HashMap();
167     // tracks all containing windows, for auto-owned dialogs
168     private LinkedList containingWindows = new LinkedList();
169 
170     public SwingBuilder() {
171         registerWidgets();
172     }
173 
174     public Object getProperty(String name) {
175         Object widget = widgets.get(name);
176         if (widget == null) {
177             return super.getProperty(name);
178         }
179         return widget;
180     }
181 
182     protected void setParent(Object parent, Object child) {
183         if (child instanceof Action) {
184             Action action = (Action) child;
185             try {
186                 InvokerHelper.setProperty(parent, "action", action);
187             } catch (RuntimeException re) {
188                 // must not have an action property...
189                 // so we ignore it and go on
190             }
191             Object keyStroke = action.getValue("KeyStroke");
192             //System.out.println("keystroke: " + keyStroke + " for: " + action);
193             if (parent instanceof JComponent) {
194                 JComponent component = (JComponent) parent;
195                 KeyStroke stroke = null;
196                 if (keyStroke instanceof String) {
197                     stroke = KeyStroke.getKeyStroke((String) keyStroke);
198                 }
199                 else if (keyStroke instanceof KeyStroke) {
200                     stroke = (KeyStroke) keyStroke;
201                 }
202                 if (stroke != null) {
203                     String key = action.toString();
204                     component.getInputMap().put(stroke, key);
205                     component.getActionMap().put(key, action);
206                 }
207             }
208         }
209         else if (child instanceof LayoutManager) {
210             if (parent instanceof RootPaneContainer) {
211                 RootPaneContainer rpc = (RootPaneContainer) parent;
212                 parent = rpc.getContentPane();
213             }
214             InvokerHelper.setProperty(parent, "layout", child);
215         }
216         else if (child instanceof JToolTip && parent instanceof JComponent) {
217             ((JToolTip)child).setComponent((JComponent)parent);
218         }
219         else if (parent instanceof JTable && child instanceof TableColumn) {
220             JTable table = (JTable) parent;
221             TableColumn column = (TableColumn) child;
222             table.addColumn(column);
223         }
224         else if (parent instanceof JTabbedPane && child instanceof Component) {
225             JTabbedPane tabbedPane = (JTabbedPane) parent;
226             tabbedPane.add((Component)child);
227         } 
228         else if (child instanceof Window) {
229             // do nothing.  owner of window is set elsewhere, and this 
230             // shouldn't get added to any parent as a child 
231             // if it is a top level component anyway
232         }
233         else { 
234             Component component = null;
235             if (child instanceof Component) {
236                 component = (Component) child;
237             }
238             else if (child instanceof ComponentFacade) {
239                 ComponentFacade facade = (ComponentFacade) child;
240                 component = facade.getComponent();
241             }
242             if (component != null) {
243                 if (parent instanceof JFrame && component instanceof JMenuBar) {
244                     JFrame frame = (JFrame) parent;
245                     frame.setJMenuBar((JMenuBar) component);
246                 }
247                 else if (parent instanceof RootPaneContainer) {
248                     RootPaneContainer rpc = (RootPaneContainer) parent;
249                     if (constraints != null) {
250                     	rpc.getContentPane().add(component, constraints);
251                     } else {
252                     	rpc.getContentPane().add(component);
253                     }
254                 }
255                 else if (parent instanceof JScrollPane) {
256                     JScrollPane scrollPane = (JScrollPane) parent;
257                     if (child instanceof JViewport) {
258                         scrollPane.setViewport((JViewport)component);
259                     } 
260                     else {
261                         scrollPane.setViewportView(component);
262                     }
263                 }
264                 else if (parent instanceof JSplitPane) {
265                     JSplitPane splitPane = (JSplitPane) parent;
266                     if (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
267                         if (splitPane.getTopComponent() == null) {
268                             splitPane.setTopComponent(component);
269                         }
270                         else {
271                             splitPane.setBottomComponent(component);
272                         }
273                     }
274                     else {
275                         if (splitPane.getLeftComponent() == null) {
276                             splitPane.setLeftComponent(component);
277                         }
278                         else {
279                             splitPane.setRightComponent(component);
280                         }
281                     }
282                 }
283                 else if (parent instanceof JMenuBar && component instanceof JMenu) {
284                     JMenuBar menuBar = (JMenuBar) parent;
285                     menuBar.add((JMenu) component);
286                 }
287                 else if (parent instanceof Container) {
288                     Container container = (Container) parent;
289                     if (constraints != null) {
290                         container.add(component, constraints);
291                     }
292                     else {
293                         container.add(component);
294                     }
295                 }
296                 else if (parent instanceof ContainerFacade) {
297                     ContainerFacade facade = (ContainerFacade) parent;
298                     facade.addComponent(component);
299                 }
300             }
301         }
302     }
303 
304     protected void nodeCompleted(Object parent, Object node) {
305         // set models after the node has been completed
306         if (node instanceof TableModel && parent instanceof JTable) {
307             JTable table = (JTable) parent;
308             TableModel model = (TableModel) node;
309             table.setModel(model);
310         }
311         if (node instanceof Startable) {
312             Startable startable = (Startable) node;
313             startable.start();
314         }
315         if (node instanceof Window) {
316             if (!containingWindows.isEmpty() && containingWindows.getLast() == node) {
317                 containingWindows.removeLast();
318             }
319         }
320     }
321 
322     protected Object createNode(Object name) {
323         return createNode(name, Collections.EMPTY_MAP);
324     }
325 
326     protected Object createNode(Object name, Object value) {
327         if (passThroughNodes.containsKey(name) && (value != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(value.getClass())) {
328             // value may need to go into containing windows list
329             if (value instanceof Window) {
330                 containingWindows.add(value);
331             }
332             return value;
333         }
334         else if (value instanceof String) {
335             Object widget = createNode(name);
336             if (widget != null) {
337                 InvokerHelper.invokeMethod(widget, "setText", value);
338             }
339             return widget;
340         }
341         else {
342         	throw new MissingMethodException((String) name, getClass(), new Object[] {value});
343         }
344     }
345 
346     protected Object createNode(Object name, Map attributes, Object value) {
347         if (passThroughNodes.containsKey(name) && (value != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(value.getClass())) {
348             // value may need to go into containing windows list
349             if (value instanceof Window) {
350                 containingWindows.add(value);
351             }
352             handleWidgetAttributes(value, attributes);
353             return value;
354         }
355         else { 
356             Object widget = createNode(name, attributes);
357             if (widget != null) {
358                 InvokerHelper.invokeMethod(widget, "setText", value.toString());
359             }
360             return widget;
361         }
362     }
363     
364     protected Object createNode(Object name, Map attributes) {
365         String widgetName = (String) attributes.remove("id");
366         constraints = attributes.remove("constraints");
367         Object widget = null;
368         if (passThroughNodes.containsKey(name)) {
369             widget = attributes.get(name);
370             if ((widget != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(widget.getClass())) {
371                 // value may need to go into containing windows list
372                 if (widget instanceof Window) {
373                     containingWindows.add(widget);
374                 }
375                 attributes.remove(name);
376             }
377             else {
378                 widget = null;
379             }
380         }
381         if (widget == null) {
382             Factory factory = (Factory) factories.get(name);
383             if (factory != null) {
384                 try {
385                     widget = factory.newInstance(attributes);
386                     if (widgetName != null) {
387                         widgets.put(widgetName, widget);
388                     }
389                     if (widget == null) {
390                         log.log(Level.WARNING, "Factory for name: " + name + " returned null");
391                     }
392                     else {
393                         if (log.isLoggable(Level.FINE)) {
394                             log.fine("For name: " + name + " created widget: " + widget);
395                         }
396                     }
397                 }
398                 catch (Exception e) {
399                     throw new RuntimeException("Failed to create component for" + name + " reason: " + e, e);
400                 }
401             }
402             else {
403                 log.log(Level.WARNING, "Could not find match for name: " + name);
404             }
405         }
406         handleWidgetAttributes(widget, attributes);
407         return widget;
408     }
409 
410     protected void handleWidgetAttributes(Object widget, Map attributes) {
411         if (widget != null) {
412             if (widget instanceof Action) {
413                 /*** @todo we could move this custom logic into the MetaClass for Action */
414                 Action action = (Action) widget;
415 
416                 Closure closure = (Closure) attributes.remove("closure");
417                 if (closure != null && action instanceof DefaultAction) {
418                     DefaultAction defaultAction = (DefaultAction) action;
419                     defaultAction.setClosure(closure);
420                 }
421 
422                 Object accel = attributes.remove("accelerator");
423                 KeyStroke stroke = null;
424                 if (accel instanceof KeyStroke) {
425                     stroke = (KeyStroke) accel;
426                 } else if (accel != null) {
427                     stroke = KeyStroke.getKeyStroke(accel.toString());
428                 }
429                 action.putValue(Action.ACCELERATOR_KEY, stroke);
430 
431                 Object mnemonic = attributes.remove("mnemonic");
432                 if ((mnemonic != null) && !(mnemonic instanceof Number)) {
433                     mnemonic = new Integer(mnemonic.toString().charAt(0));
434                 }
435                 action.putValue(Action.MNEMONIC_KEY, mnemonic);
436 
437                 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
438                     Map.Entry entry = (Map.Entry) iter.next();
439                     String actionName = (String) entry.getKey();    // todo dk: misleading naming. this can be any property name
440 
441                     // typically standard Action names start with upper case, so lets upper case it            
442                     actionName = capitalize(actionName);            // todo dk: in general, this shouldn't be capitalized
443                     Object value = entry.getValue();
444 
445                     action.putValue(actionName, value);
446                 }
447 
448             }
449             else {
450                 // some special cases...
451                 if (attributes.containsKey("buttonGroup")) {
452                     Object o = attributes.get("buttonGroup");
453                     if ((o instanceof ButtonGroup) && (widget instanceof AbstractButton)) {
454                         ((AbstractButton)widget).getModel().setGroup((ButtonGroup)o);
455                         attributes.remove("buttonGroup");
456                     }
457                 }
458 
459                 // this next statement nd if/else is a workaround until GROOVY-305 is fixed
460                 Object mnemonic = attributes.remove("mnemonic");
461                 if ((mnemonic != null) && (mnemonic instanceof Number)) {
462                     InvokerHelper.setProperty(widget, "mnemonic", new Character((char)((Number)mnemonic).intValue()));
463                 } 
464                 else if (mnemonic != null) {
465                     InvokerHelper.setProperty(widget, "mnemonic", new Character(mnemonic.toString().charAt(0)));
466                 } 
467 
468                 // set the properties
469                 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
470                     Map.Entry entry = (Map.Entry) iter.next();
471                     String property = entry.getKey().toString();
472                     Object value = entry.getValue();
473                     InvokerHelper.setProperty(widget, property, value);
474                 }
475             }
476         }
477     }
478 
479     protected String capitalize(String text) {
480         char ch = text.charAt(0);
481         if (Character.isUpperCase(ch)) {
482             return text;
483         }
484         StringBuffer buffer = new StringBuffer(text.length());
485         buffer.append(Character.toUpperCase(ch));
486         buffer.append(text.substring(1));
487         return buffer.toString();
488     }
489 
490     protected void registerWidgets() {
491         //
492         // non-widget support classes
493         //
494         registerBeanFactory("action", DefaultAction.class);
495         passThroughNodes.put("action", javax.swing.Action.class);
496         registerBeanFactory("buttonGroup", ButtonGroup.class);
497         registerFactory("map", new Factory() {      // todo dk: is that still needed?
498             public Object newInstance(Map properties)
499                 throws InstantiationException, InstantiationException, IllegalAccessException {
500                 return properties;
501             }
502         });
503         // ulimate pass through type
504         passThroughNodes.put("widget", java.awt.Component.class);
505 
506         //
507         // standalone window classes
508         //
509         registerFactory("dialog", new Factory() {
510             public Object newInstance(Map properties)
511                 throws InstantiationException, InstantiationException, IllegalAccessException {
512                 return createDialog(properties);
513             }
514         });
515         registerFactory("frame", new Factory() {
516             public Object newInstance(Map properties)
517                 throws InstantiationException, InstantiationException, IllegalAccessException {
518                 return createFrame(properties);
519             }
520         });
521         registerBeanFactory("fileChooser", JFileChooser.class);
522         registerFactory("frame", new Factory() {        // todo dk: frame registered twice ???
523             public Object newInstance(Map properties)
524                 throws InstantiationException, InstantiationException, IllegalAccessException {
525                 return createFrame(properties);
526             }
527         });
528         registerBeanFactory("optionPane", JOptionPane.class);
529         registerFactory("window", new Factory() {
530             public Object newInstance(Map properties)
531                 throws InstantiationException, InstantiationException, IllegalAccessException {
532                 return createWindow(properties);
533             }
534         });
535         
536         //
537         // widgets
538         //
539         registerBeanFactory("button", JButton.class);
540         registerBeanFactory("checkBox", JCheckBox.class);
541         registerBeanFactory("checkBoxMenuItem", JCheckBoxMenuItem.class);
542         registerBeanFactory("colorChooser", JColorChooser.class);
543         registerFactory("comboBox", new Factory() {
544             public Object newInstance(Map properties)
545                 throws InstantiationException, InstantiationException, IllegalAccessException {
546                 return createComboBox(properties);
547             }
548         });
549         registerBeanFactory("desktopPane", JDesktopPane.class);
550         registerBeanFactory("editorPane", JEditorPane.class);
551         registerFactory("formattedTextField", new Factory() {
552             public Object newInstance(Map properties)
553                 throws InstantiationException, InstantiationException, IllegalAccessException {
554                 return createFormattedTextField(properties);
555             }
556         });
557         registerBeanFactory("internalFrame", JInternalFrame.class);
558         registerBeanFactory("label", JLabel.class);
559         registerBeanFactory("layeredPane", JLayeredPane.class);
560         registerBeanFactory("list", JList.class);
561         registerBeanFactory("menu", JMenu.class);
562         registerBeanFactory("menuBar", JMenuBar.class);
563         registerBeanFactory("menuItem", JMenuItem.class);
564         registerBeanFactory("panel", JPanel.class);
565         registerBeanFactory("passwordField", JPasswordField.class);
566         registerBeanFactory("popupMenu", JPopupMenu.class);
567         registerBeanFactory("progressBar", JProgressBar.class);
568         registerBeanFactory("radioButton", JRadioButton.class);
569         registerBeanFactory("radioButtonMenuItem", JRadioButtonMenuItem.class);
570         registerBeanFactory("scrollBar", JScrollBar.class);
571         registerBeanFactory("scrollPane", JScrollPane.class);
572         registerBeanFactory("separator", JSeparator.class);
573         registerBeanFactory("slider", JSlider.class);
574         registerBeanFactory("spinner", JSpinner.class);
575         registerFactory("splitPane", new Factory() {
576             public Object newInstance(Map properties) {
577                 JSplitPane answer = new JSplitPane();
578                 answer.setLeftComponent(null);
579                 answer.setRightComponent(null);
580                 answer.setTopComponent(null);
581                 answer.setBottomComponent(null);
582                 return answer;
583             }
584         });
585         registerBeanFactory("tabbedPane", JTabbedPane.class);
586         registerBeanFactory("table", JTable.class);
587         registerBeanFactory("textArea", JTextArea.class);
588         registerBeanFactory("textPane", JTextPane.class);
589         registerBeanFactory("textField", JTextField.class);
590         registerBeanFactory("toggleButton", JToggleButton.class);
591         registerBeanFactory("toolBar", JToolBar.class);
592         //registerBeanFactory("tooltip", JToolTip.class); // doens't work, user toolTipText property
593         registerBeanFactory("tree", JTree.class);
594         registerBeanFactory("viewport", JViewport.class); // sub class?
595 
596         //
597         // MVC models   
598         //
599         registerBeanFactory("boundedRangeModel", DefaultBoundedRangeModel.class);
600 
601         // spinner models
602 	registerBeanFactory("spinnerDateModel", SpinnerDateModel.class);
603         registerBeanFactory("spinnerListModel", SpinnerListModel.class);
604         registerBeanFactory("spinnerNumberModel", SpinnerNumberModel.class);
605 
606 	// table models
607         registerFactory("tableModel", new Factory() {
608             public Object newInstance(Map properties) {
609                 ValueModel model = (ValueModel) properties.remove("model");
610                 if (model == null) {
611                     Object list = properties.remove("list");
612                     if (list == null) {
613                         list = new ArrayList();
614                     }
615                     model = new ValueHolder(list);
616                 }
617                 return new DefaultTableModel(model);
618             }
619         });
620         passThroughNodes.put("tableModel", javax.swing.table.TableModel.class);
621 
622         registerFactory("propertyColumn", new Factory() {
623             public Object newInstance(Map properties) {
624                 Object current = getCurrent();
625                 if (current instanceof DefaultTableModel) {
626                     DefaultTableModel model = (DefaultTableModel) current;
627                     Object header = properties.remove("header");
628                     if (header == null) {
629                         header = "";
630                     }
631                     String property = (String) properties.remove("propertyName");
632                     if (property == null) {
633                         throw new IllegalArgumentException("Must specify a property for a propertyColumn");
634                     }
635                     Class type = (Class) properties.remove("type");
636                     if (type == null) {
637                         type = Object.class;
638                     }
639                     return model.addPropertyColumn(header, property, type);
640                 }
641                 else {
642                     throw new RuntimeException("propertyColumn must be a child of a tableModel");
643                 }
644             }
645         });
646 
647         registerFactory("closureColumn", new Factory() {
648             public Object newInstance(Map properties) {
649                 Object current = getCurrent();
650                 if (current instanceof DefaultTableModel) {
651                     DefaultTableModel model = (DefaultTableModel) current;
652                     Object header = properties.remove("header");
653                     if (header == null) {
654                         header = "";
655                     }
656                     Closure readClosure = (Closure) properties.remove("read");
657                     if (readClosure == null) {
658                         throw new IllegalArgumentException("Must specify 'read' Closure property for a closureColumn");
659                     }
660                     Closure writeClosure = (Closure) properties.remove("write");
661                     Class type = (Class) properties.remove("type");
662                     if (type == null) {
663                         type = Object.class;
664                     }
665                     return model.addClosureColumn(header, readClosure, writeClosure, type);
666                 }
667                 else {
668                     throw new RuntimeException("propertyColumn must be a child of a tableModel");
669                 }
670             }
671         });
672 
673 
674         //Standard Layouts
675         registerBeanFactory("borderLayout", BorderLayout.class);
676         registerBeanFactory("cardLayout", CardLayout.class);
677         registerBeanFactory("flowLayout", FlowLayout.class);
678         registerBeanFactory("gridBagLayout", GridBagLayout.class);
679         registerBeanFactory("gridLayout", GridLayout.class);
680         registerBeanFactory("overlayLayout", OverlayLayout.class);
681         registerBeanFactory("springLayout", SpringLayout.class);
682         registerBeanFactory("gridBagConstraints", GridBagConstraints.class);
683         registerBeanFactory("gbc", GridBagConstraints.class); // shortcut name
684 
685         // box layout
686         registerFactory("boxLayout", new Factory() {
687             public Object newInstance(Map properties)
688                 throws InstantiationException, InstantiationException, IllegalAccessException {
689                 return createBoxLayout(properties);
690             }
691         });
692 
693         // Box related layout components
694         registerFactory("hbox", new Factory() {
695             public Object newInstance(Map properties) {
696                 return Box.createHorizontalBox();
697             }
698         });
699         registerFactory("hglue", new Factory() {
700             public Object newInstance(Map properties) {
701                 return Box.createHorizontalGlue();
702             }
703         });
704         registerFactory("hstrut", new Factory() {
705             public Object newInstance(Map properties) {
706                 try {
707                 Object num = properties.remove("width");
708                 if (num instanceof Number) {
709                     return Box.createHorizontalStrut(((Number)num).intValue());
710                 } else {
711                     return Box.createHorizontalStrut(6);
712                 }
713                 } catch (RuntimeException re) {
714                     re.printStackTrace(System.out);
715                     throw re;
716                 }
717             }
718         });
719         registerFactory("vbox", new Factory() {
720             public Object newInstance(Map properties) {
721                 return Box.createVerticalBox();
722             }
723         });
724         registerFactory("vglue", new Factory() {
725             public Object newInstance(Map properties) {
726                 return Box.createVerticalGlue();
727             }
728         });
729         registerFactory("vstrut", new Factory() {
730             public Object newInstance(Map properties) {
731                 Object num = properties.remove("height");
732                 if (num instanceof Number) {
733                     return Box.createVerticalStrut(((Number)num).intValue());
734                 } else {
735                     return Box.createVerticalStrut(6);
736                 }
737             }
738         });
739         registerFactory("glue", new Factory() {
740             public Object newInstance(Map properties) {
741                 return Box.createGlue();
742             }
743         });
744         registerFactory("rigidArea", new Factory() {
745             public Object newInstance(Map properties) {
746                 Dimension dim;
747                 Object o = properties.remove("size");
748                 if (o instanceof Dimension) {
749                     dim = (Dimension) o;
750                 } else {
751                     int w, h;
752                     o = properties.remove("width");
753                     w = ((o instanceof Number)) ? ((Number)o).intValue() : 6;
754                     o = properties.remove("height");
755                     h = ((o instanceof Number)) ? ((Number)o).intValue() : 6;
756                     dim = new Dimension(w, h);
757                 }
758                 return Box.createRigidArea(dim);
759             }
760         });
761         
762         // table layout
763         registerBeanFactory("tableLayout", TableLayout.class);
764         registerFactory("tr", new Factory() {
765             public Object newInstance(Map properties) {
766                 Object parent = getCurrent();
767                 if (parent instanceof TableLayout) {
768                     return new TableLayoutRow((TableLayout) parent);
769                 }
770                 else {
771                     throw new RuntimeException("'tr' must be within a 'tableLayout'");
772                 }
773             }
774         });
775         registerFactory("td", new Factory() {
776             public Object newInstance(Map properties) {
777                 Object parent = getCurrent();
778                 if (parent instanceof TableLayoutRow) {
779                     return new TableLayoutCell((TableLayoutRow) parent);
780                 }
781                 else {
782                     throw new RuntimeException("'td' must be within a 'tr'");
783                 }
784             }
785         });
786     }
787 
788     protected Object createBoxLayout(Map properties) {
789         Object parent = getCurrent();
790         if (parent instanceof Container) {
791             Object axisObject = properties.remove("axis");
792             int axis = BoxLayout.X_AXIS;
793             if (axisObject != null) {
794                 Integer i = (Integer) axisObject;
795                 axis = i.intValue();
796             }
797             
798             Container target = (Container) parent;
799             if (target instanceof RootPaneContainer) {
800             	target = ((RootPaneContainer) target).getContentPane();
801             }
802             BoxLayout answer = new BoxLayout(target, axis);
803             
804             // now lets try set the layout property
805             InvokerHelper.setProperty(parent, "layout", answer);
806             return answer;
807         }
808         else {
809             throw new RuntimeException("Must be nested inside a Container");
810         }
811     }
812 
813     protected Object createDialog(Map properties) {
814         JDialog dialog;
815         Object owner = properties.remove("owner");
816         // if owner not explicit, use the last window type in the list
817         if ((owner == null) && !containingWindows.isEmpty()) {
818             owner = containingWindows.getLast();
819         }
820         if (owner instanceof Frame) {
821             dialog = new JDialog((Frame) owner);
822         }
823         else if (owner instanceof Dialog) {
824             dialog = new JDialog((Dialog) owner);
825         }
826         else {
827             dialog = new JDialog();
828         }
829         containingWindows.add(dialog);
830         return dialog;
831     }
832     
833     /***
834      * Uses 'format," or "value,"  (in order)
835      *
836      */
837     protected Object createFormattedTextField(Map properties) {
838         JFormattedTextField ftf;
839         if (properties.containsKey("format")) {
840             ftf = new JFormattedTextField((Format) properties.remove("format"));
841         }
842         else if (properties.containsKey("value")) {
843             ftf = new JFormattedTextField(properties.remove("value"));
844         }
845         else {
846             ftf = new JFormattedTextField();
847         }
848         return ftf;
849     }
850 
851     protected Object createFrame(Map properties) {
852         JFrame frame = new JFrame();
853         containingWindows.add(frame);
854         return frame;
855     }
856     
857     protected Object createWindow(Map properties) {
858         JWindow window;
859         Object owner = properties.remove("owner");
860         // if owner not explicit, use the last window type in the list
861         if ((owner == null) && !containingWindows.isEmpty()) {
862             owner = containingWindows.getLast();
863         }
864         if (owner instanceof Frame) {
865             window = new JWindow((Frame) owner);
866         }
867         else if (owner instanceof Window) {
868             window = new JWindow((Window) owner);
869         }
870         else {
871             window = new JWindow();
872         }
873         containingWindows.add(window);
874         return window;
875     }
876 
877     protected Object createComboBox(Map properties) {
878         Object items = properties.remove("items");
879         if (items instanceof Vector) {
880             return new JComboBox((Vector) items);
881         }
882         else if (items instanceof List) {
883             List list = (List) items;
884             return new JComboBox(list.toArray());
885         }
886         else if (items instanceof Object[]) {
887             return new JComboBox((Object[]) items);
888         }
889         else {
890             return new JComboBox();
891         }
892     }
893 
894     protected void registerBeanFactory(String name, final Class beanClass) {
895         registerFactory(name, new Factory() {
896             public Object newInstance(Map properties) throws InstantiationException, IllegalAccessException {
897                 return beanClass.newInstance();
898             }
899         });
900 
901     }
902 
903     protected void registerFactory(String name, Factory factory) {
904         factories.put(name, factory);
905     }
906 }