View Javadoc

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