View Javadoc

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