View Javadoc

1   /*
2    $Id: SwingBuilder.java,v 1.8 2004/07/14 19:12:39 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  import groovy.model.DefaultTableModel;
50  import groovy.model.ValueHolder;
51  import groovy.model.ValueModel;
52  import groovy.swing.impl.ComponentFacade;
53  import groovy.swing.impl.ContainerFacade;
54  import groovy.swing.impl.DefaultAction;
55  import groovy.swing.impl.Factory;
56  import groovy.swing.impl.Startable;
57  import groovy.swing.impl.TableLayout;
58  import groovy.swing.impl.TableLayoutCell;
59  import groovy.swing.impl.TableLayoutRow;
60  import groovy.util.BuilderSupport;
61  
62  import java.awt.Component;
63  import java.awt.Container;
64  import java.awt.Dimension;
65  import java.awt.Dialog;
66  import java.awt.Frame;
67  import java.awt.LayoutManager;
68  import java.util.ArrayList;
69  import java.util.Collections;
70  import java.util.HashMap;
71  import java.util.HashSet;
72  import java.util.Iterator;
73  import java.util.List;
74  import java.util.Map;
75  import java.util.Set;
76  import java.util.Vector;
77  import java.util.logging.Level;
78  import java.util.logging.Logger;
79  
80  import javax.swing.AbstractButton;
81  import javax.swing.Action;
82  import javax.swing.Box;
83  import javax.swing.BoxLayout;
84  import javax.swing.ButtonGroup;
85  import javax.swing.JButton;
86  import javax.swing.JCheckBox;
87  import javax.swing.JCheckBoxMenuItem;
88  import javax.swing.JComboBox;
89  import javax.swing.JComponent;
90  import javax.swing.JDesktopPane;
91  import javax.swing.JDialog;
92  import javax.swing.JEditorPane;
93  import javax.swing.JFileChooser;
94  import javax.swing.JFrame;
95  import javax.swing.JInternalFrame;
96  import javax.swing.JLabel;
97  import javax.swing.JList;
98  import javax.swing.JMenu;
99  import javax.swing.JMenuBar;
100 import javax.swing.JMenuItem;
101 import javax.swing.JOptionPane;
102 import javax.swing.JPanel;
103 import javax.swing.JPasswordField;
104 import javax.swing.JPopupMenu;
105 import javax.swing.JProgressBar;
106 import javax.swing.JRadioButton;
107 import javax.swing.JRadioButtonMenuItem;
108 import javax.swing.JScrollPane;
109 import javax.swing.JSeparator;
110 import javax.swing.JSplitPane;
111 import javax.swing.JTabbedPane;
112 import javax.swing.JTable;
113 import javax.swing.JTextArea;
114 import javax.swing.JTextField;
115 import javax.swing.JTextPane;
116 import javax.swing.JToggleButton;
117 import javax.swing.JToolBar;
118 import javax.swing.JTree;
119 import javax.swing.KeyStroke;
120 import javax.swing.RootPaneContainer;
121 import javax.swing.table.TableColumn;
122 import javax.swing.table.TableModel;
123 
124 import org.codehaus.groovy.runtime.InvokerHelper;
125 
126 /***
127  * A helper class for creating Swing widgets using GroovyMarkup
128  * 
129  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
130  * @version $Revision: 1.8 $
131  */
132 public class SwingBuilder extends BuilderSupport {
133 
134     private Logger log = Logger.getLogger(getClass().getName());
135     private Map factories = new HashMap();
136     private Object constraints;
137     private Map passThroughNodes = new HashMap();
138 
139     public SwingBuilder() {
140         registerWidgets();
141     }
142 
143     protected void setParent(Object parent, Object child) {
144         if (child instanceof Action) {
145             Action action = (Action) child;
146             try {
147                 InvokerHelper.setProperty(parent, "action", action);
148             } catch (RuntimeException re) {
149                 // must not have an action property...
150                 // so we ignore it and go on
151             }
152             Object keyStroke = action.getValue("KeyStroke");
153             //System.out.println("keystroke: " + keyStroke + " for: " + action);
154             if (parent instanceof JComponent) {
155                 JComponent component = (JComponent) parent;
156                 KeyStroke stroke = null;
157                 if (keyStroke instanceof String) {
158                     stroke = KeyStroke.getKeyStroke((String) keyStroke);
159                 }
160                 else if (keyStroke instanceof KeyStroke) {
161                     stroke = (KeyStroke) keyStroke;
162                 }
163                 if (stroke != null) {
164                     String key = action.toString();
165                     component.getInputMap().put(stroke, key);
166                     component.getActionMap().put(key, action);
167                 }
168             }
169         }
170         else if (child instanceof LayoutManager) {
171             if (parent instanceof RootPaneContainer) {
172                 RootPaneContainer rpc = (RootPaneContainer) parent;
173                 parent = rpc.getContentPane();
174             }
175             InvokerHelper.setProperty(parent, "layout", child);
176         }
177         else if (parent instanceof JTable && child instanceof TableColumn) {
178             JTable table = (JTable) parent;
179             TableColumn column = (TableColumn) child;
180             table.addColumn(column);
181         }
182         else if (parent instanceof JTabbedPane && child instanceof Component) {
183             JTabbedPane tabbedPane = (JTabbedPane) parent;
184             tabbedPane.add((Component)child);
185         }
186         else {
187             Component component = null;
188             if (child instanceof Component) {
189                 component = (Component) child;
190             }
191             else if (child instanceof ComponentFacade) {
192                 ComponentFacade facade = (ComponentFacade) child;
193                 component = facade.getComponent();
194             }
195             if (component != null) {
196                 if (parent instanceof JFrame && component instanceof JMenuBar) {
197                     JFrame frame = (JFrame) parent;
198                     frame.setJMenuBar((JMenuBar) component);
199                 }
200                 else if (parent instanceof RootPaneContainer) {
201                     RootPaneContainer rpc = (RootPaneContainer) parent;
202                     rpc.getContentPane().add(component);
203                 }
204                 else if (parent instanceof JScrollPane) {
205                     JScrollPane scrollPane = (JScrollPane) parent;
206                     scrollPane.setViewportView(component);
207                 }
208                 else if (parent instanceof JSplitPane) {
209                     JSplitPane splitPane = (JSplitPane) parent;
210                     if (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
211                         if (splitPane.getTopComponent() == null) {
212                             splitPane.setTopComponent(component);
213                         }
214                         else {
215                             splitPane.setBottomComponent(component);
216                         }
217                     }
218                     else {
219                         if (splitPane.getLeftComponent() == null) {
220                             splitPane.setLeftComponent(component);
221                         }
222                         else {
223                             splitPane.setRightComponent(component);
224                         }
225                     }
226                 }
227                 else if (parent instanceof JMenuBar && component instanceof JMenu) {
228                     JMenuBar menuBar = (JMenuBar) parent;
229                     menuBar.add((JMenu) component);
230                 }
231                 else if (parent instanceof Container) {
232                     Container container = (Container) parent;
233                     if (constraints != null) {
234                         container.add(component, constraints);
235                     }
236                     else {
237                         container.add(component);
238                     }
239                 }
240                 else if (parent instanceof ContainerFacade) {
241                     ContainerFacade facade = (ContainerFacade) parent;
242                     facade.addComponent(component);
243                 }
244             }
245         }
246     }
247 
248     protected void nodeCompleted(Object parent, Object node) {
249         // set models after the node has been completed
250         if (node instanceof TableModel && parent instanceof JTable) {
251             JTable table = (JTable) parent;
252             TableModel model = (TableModel) node;
253             table.setModel(model);
254         }
255         if (node instanceof Startable) {
256             Startable startable = (Startable) node;
257             startable.start();
258         }
259     }
260 
261     protected Object createNode(Object name) {
262         return createNode(name, Collections.EMPTY_MAP);
263     }
264 
265     protected Object createNode(Object name, Object value) {
266         if (passThroughNodes.containsKey(name) && (value != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(value.getClass())) {
267             return value;
268         }
269         else {
270             Object widget = createNode(name);
271             if (widget != null && value instanceof String) {
272                 InvokerHelper.invokeMethod(widget, "setText", value);
273             }
274             return widget;
275         }
276     }
277 
278     protected Object createNode(Object name, Map attributes, Object value) {
279         if (passThroughNodes.containsKey(name) && (value != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(value.getClass())) {
280             handleWidgetAttributes(value, attributes);
281             return value;
282         }
283         else { 
284             Object widget = createNode(name, attributes);
285             if (widget != null) {
286                 InvokerHelper.invokeMethod(widget, "setText", value.toString());
287             }
288             return widget;
289         }
290     }
291     
292     protected Object createNode(Object name, Map attributes) {
293         constraints = attributes.remove("constraints");
294         Object widget = null;
295         if (passThroughNodes.containsKey(name)) {
296             widget = attributes.get(name);
297             if ((widget != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(widget.getClass())) {
298                 attributes.remove(name);
299             }
300             else {
301                 widget = null;
302             }
303         }
304         if (widget == null) {
305             Factory factory = (Factory) factories.get(name);
306             if (factory != null) {
307                 try {
308                     widget = factory.newInstance(attributes);
309                     if (widget == null) {
310                         log.log(Level.WARNING, "Factory for name: " + name + " returned null");
311                     }
312                     else {
313                         if (log.isLoggable(Level.FINE)) {
314                             log.fine("For name: " + name + " created widget: " + widget);
315                         }
316                     }
317                 }
318                 catch (Exception e) {
319                     throw new RuntimeException("Failed to create component for" + name + " reason: " + e, e);
320                 }
321             }
322             else {
323                 log.log(Level.WARNING, "Could not find match for name: " + name);
324             }
325         }
326         handleWidgetAttributes(widget, attributes);
327         return widget;
328     }
329 
330     protected void handleWidgetAttributes(Object widget, Map attributes) {
331         if (widget != null) {
332             if (widget instanceof Action) {
333                 /*** @todo we could move this custom logic into the MetaClass for Action */
334                 Action action = (Action) widget;
335 
336                 Closure closure = (Closure) attributes.remove("closure");
337                 if (closure != null && action instanceof DefaultAction) {
338                     DefaultAction defaultAction = (DefaultAction) action;
339                     defaultAction.setClosure(closure);
340                 }
341 
342                 Object accel = attributes.remove("accelerator");
343                 KeyStroke stroke = null;
344                 if (accel instanceof KeyStroke) {
345                     stroke = (KeyStroke) accel;
346                 } else if (accel != null) {
347                     stroke = KeyStroke.getKeyStroke(accel.toString());
348                 }
349                 action.putValue(Action.ACCELERATOR_KEY, stroke);
350 
351                 Object mnemonic = attributes.remove("mnemonic");
352                 if ((mnemonic != null) && !(mnemonic instanceof Number)) {
353                     mnemonic = new Integer(mnemonic.toString().charAt(0));
354                 }
355                 action.putValue(Action.MNEMONIC_KEY, mnemonic);
356 
357                 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
358                     Map.Entry entry = (Map.Entry) iter.next();
359                     String actionName = (String) entry.getKey();
360 
361                     // typically standard Action names start with upper case, so lets upper case it            
362                     actionName = capitalize(actionName);
363                     Object value = entry.getValue();
364 
365                     action.putValue(actionName, value);
366                 }
367 
368             }
369             else {
370                 // some special cases...
371                 if (attributes.containsKey("buttonGroup")) {
372                     Object o = attributes.get("buttonGroup");
373                     if ((o instanceof ButtonGroup) && (widget instanceof AbstractButton)) {
374                         ((AbstractButton)widget).getModel().setGroup((ButtonGroup)o);
375                         attributes.remove("buttonGroup");
376                     }
377                 }
378 
379                 // this next statement nd if/else is a workaround until GROOVY-305 is fixed
380                 Object mnemonic = attributes.remove("mnemonic");
381                 if ((mnemonic != null) && (mnemonic instanceof Number)) {
382                     InvokerHelper.setProperty(widget, "mnemonic", new Character((char)((Number)mnemonic).intValue()));
383                 } 
384                 else if (mnemonic != null) {
385                     InvokerHelper.setProperty(widget, "mnemonic", new Character(mnemonic.toString().charAt(0)));
386                 } 
387 
388                 // set the properties
389                 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
390                     Map.Entry entry = (Map.Entry) iter.next();
391                     String property = entry.getKey().toString();
392                     Object value = entry.getValue();
393                     InvokerHelper.setProperty(widget, property, value);
394                 }
395             }
396         }
397     }
398 
399     protected String capitalize(String text) {
400         char ch = text.charAt(0);
401         if (Character.isUpperCase(ch)) {
402             return text;
403         }
404         StringBuffer buffer = new StringBuffer(text.length());
405         buffer.append(Character.toUpperCase(ch));
406         buffer.append(text.substring(1));
407         return buffer.toString();
408     }
409 
410     protected void registerWidgets() {
411         registerBeanFactory("action", DefaultAction.class);
412         passThroughNodes.put("action", javax.swing.Action.class);
413         registerFactory("boxLayout", new Factory() {
414             public Object newInstance(Map properties)
415                 throws InstantiationException, InstantiationException, IllegalAccessException {
416                 return createBoxLayout(properties);
417             }
418         });
419         registerBeanFactory("button", JButton.class);
420         registerBeanFactory("buttonGroup", ButtonGroup.class);
421         registerBeanFactory("checkBox", JCheckBox.class);
422         registerBeanFactory("checkBoxMenuItem", JCheckBoxMenuItem.class);
423 
424         registerFactory("comboBox", new Factory() {
425             public Object newInstance(Map properties)
426                 throws InstantiationException, InstantiationException, IllegalAccessException {
427                 return createComboBox(properties);
428             }
429         });
430 
431         registerBeanFactory("desktopPane", JDesktopPane.class);
432 
433         registerFactory("dialog", new Factory() {
434             public Object newInstance(Map properties)
435                 throws InstantiationException, InstantiationException, IllegalAccessException {
436                 return createDialog(properties);
437             }
438         });
439 
440         registerBeanFactory("editorPane", JEditorPane.class);
441         registerBeanFactory("fileChooser", JFileChooser.class);
442         registerBeanFactory("frame", JFrame.class);
443         registerBeanFactory("internalFrame", JInternalFrame.class);
444         registerBeanFactory("label", JLabel.class);
445         registerBeanFactory("list", JList.class);
446 
447         registerFactory("map", new Factory() {
448             public Object newInstance(Map properties)
449                 throws InstantiationException, InstantiationException, IllegalAccessException {
450                 return properties;
451             }
452         });
453 
454         registerBeanFactory("menu", JMenu.class);
455         registerBeanFactory("menuBar", JMenuBar.class);
456         registerBeanFactory("menuItem", JMenuItem.class);
457         registerBeanFactory("panel", JPanel.class);
458         registerBeanFactory("passwordField", JPasswordField.class);
459         registerBeanFactory("popupMenu", JPopupMenu.class);
460         registerBeanFactory("progressBar", JProgressBar.class);
461         registerBeanFactory("radioButton", JRadioButton.class);
462         registerBeanFactory("radioButtonMenuItem", JRadioButtonMenuItem.class);
463         registerBeanFactory("optionPane", JOptionPane.class);
464         registerBeanFactory("scrollPane", JScrollPane.class);
465         registerBeanFactory("separator", JSeparator.class);
466         registerBeanFactory("tabbedPane", JTabbedPane.class);
467 
468         registerFactory("splitPane", new Factory() {
469             public Object newInstance(Map properties) {
470                 JSplitPane answer = new JSplitPane();
471                 answer.setLeftComponent(null);
472                 answer.setRightComponent(null);
473                 answer.setTopComponent(null);
474                 answer.setBottomComponent(null);
475                 return answer;
476             }
477         });
478 
479         // Box related layout components
480         registerFactory("hbox", new Factory() {
481             public Object newInstance(Map properties) {
482                 return Box.createHorizontalBox();
483             }
484         });
485         registerFactory("hglue", new Factory() {
486             public Object newInstance(Map properties) {
487                 return Box.createHorizontalGlue();
488             }
489         });
490         registerFactory("hstrut", new Factory() {
491             public Object newInstance(Map properties) {
492                 try {
493                 Object num = properties.remove("width");
494                 if (num instanceof Number) {
495                     return Box.createHorizontalStrut(((Number)num).intValue());
496                 } else {
497                     return Box.createHorizontalStrut(6);
498                 }
499                 } catch (RuntimeException re) {
500                     re.printStackTrace(System.out);
501                     throw re;
502                 }
503             }
504         });
505         registerFactory("vbox", new Factory() {
506             public Object newInstance(Map properties) {
507                 return Box.createVerticalBox();
508             }
509         });
510         registerFactory("vglue", new Factory() {
511             public Object newInstance(Map properties) {
512                 return Box.createVerticalGlue();
513             }
514         });
515         registerFactory("vstrut", new Factory() {
516             public Object newInstance(Map properties) {
517                 Object num = properties.remove("height");
518                 if (num instanceof Number) {
519                     return Box.createVerticalStrut(((Number)num).intValue());
520                 } else {
521                     return Box.createVerticalStrut(6);
522                 }
523             }
524         });
525         registerFactory("glue", new Factory() {
526             public Object newInstance(Map properties) {
527                 return Box.createGlue();
528             }
529         });
530         registerFactory("rigidArea", new Factory() {
531             public Object newInstance(Map properties) {
532                 Dimension dim;
533                 Object o = properties.remove("size");
534                 if (o instanceof Dimension) {
535                     dim = (Dimension) o;
536                 } else {
537                     int w, h;
538                     o = properties.remove("width");
539                     w = ((o instanceof Number)) ? ((Number)o).intValue() : 6;
540                     o = properties.remove("height");
541                     h = ((o instanceof Number)) ? ((Number)o).intValue() : 6;
542                     dim = new Dimension(w, h);
543                 }
544                 return Box.createRigidArea(dim);
545             }
546         });
547 
548         registerBeanFactory("tabbedPane", JTabbedPane.class);
549         registerBeanFactory("table", JTable.class);
550 
551         registerBeanFactory("textArea", JTextArea.class);
552         registerBeanFactory("textPane", JTextPane.class);
553         registerBeanFactory("textField", JTextField.class);
554         registerBeanFactory("toggleButton", JToggleButton.class);
555         registerBeanFactory("tree", JTree.class);
556         registerBeanFactory("toolBar", JToolBar.class);
557 
558         // MVC models        
559         registerFactory("tableModel", new Factory() {
560             public Object newInstance(Map properties) {
561                 ValueModel model = (ValueModel) properties.remove("model");
562                 if (model == null) {
563                     Object list = properties.remove("list");
564                     if (list == null) {
565                         list = new ArrayList();
566                     }
567                     model = new ValueHolder(list);
568                 }
569                 return new DefaultTableModel(model);
570             }
571         });
572         passThroughNodes.put("tableModel", javax.swing.table.TableModel.class);
573 
574         registerFactory("propertyColumn", new Factory() {
575             public Object newInstance(Map properties) {
576                 Object current = getCurrent();
577                 if (current instanceof DefaultTableModel) {
578                     DefaultTableModel model = (DefaultTableModel) current;
579                     Object header = properties.remove("header");
580                     if (header == null) {
581                         header = "";
582                     }
583                     String property = (String) properties.remove("propertyName");
584                     if (property == null) {
585                         throw new IllegalArgumentException("Must specify a property for a propertyColumn");
586                     }
587                     Class type = (Class) properties.remove("type");
588                     if (type == null) {
589                         type = Object.class;
590                     }
591                     return model.addPropertyColumn(header, property, type);
592                 }
593                 else {
594                     throw new RuntimeException("propertyColumn must be a child of a tableModel");
595                 }
596             }
597         });
598 
599         registerFactory("closureColumn", new Factory() {
600             public Object newInstance(Map properties) {
601                 Object current = getCurrent();
602                 if (current instanceof DefaultTableModel) {
603                     DefaultTableModel model = (DefaultTableModel) current;
604                     Object header = properties.remove("header");
605                     if (header == null) {
606                         header = "";
607                     }
608                     Closure readClosure = (Closure) properties.remove("read");
609                     if (readClosure == null) {
610                         throw new IllegalArgumentException("Must specify 'read' Closure property for a closureColumn");
611                     }
612                     Closure writeClosure = (Closure) properties.remove("write");
613                     Class type = (Class) properties.remove("type");
614                     if (type == null) {
615                         type = Object.class;
616                     }
617                     return model.addClosureColumn(header, readClosure, writeClosure, type);
618                 }
619                 else {
620                     throw new RuntimeException("propertyColumn must be a child of a tableModel");
621                 }
622             }
623         });
624 
625         // table layout
626         registerBeanFactory("tableLayout", TableLayout.class);
627         registerFactory("tr", new Factory() {
628             public Object newInstance(Map properties) {
629                 Object parent = getCurrent();
630                 if (parent instanceof TableLayout) {
631                     return new TableLayoutRow((TableLayout) parent);
632                 }
633                 else {
634                     throw new RuntimeException("'tr' must be within a 'tableLayout'");
635                 }
636             }
637         });
638         registerFactory("td", new Factory() {
639             public Object newInstance(Map properties) {
640                 Object parent = getCurrent();
641                 if (parent instanceof TableLayoutRow) {
642                     return new TableLayoutCell((TableLayoutRow) parent);
643                 }
644                 else {
645                     throw new RuntimeException("'td' must be within a 'tr'");
646                 }
647             }
648         });
649 
650         //ulimlate pass through type
651         passThroughNodes.put("widget", java.awt.Component.class);
652     }
653 
654     protected Object createBoxLayout(Map properties) {
655         Object parent = getCurrent();
656         if (parent instanceof Container) {
657             Object axisObject = properties.remove("axis");
658             int axis = 0;
659             if (axisObject != null) {
660                 Integer i = (Integer) axisObject;
661                 axis = i.intValue();
662             }
663             BoxLayout answer = new BoxLayout((Container) parent, axis);
664             
665             // now lets try set the layout property
666             InvokerHelper.setProperty(parent, "layout", answer);
667             return answer;
668         }
669         else {
670             throw new RuntimeException("Must be nested inside a Container");
671         }
672     }
673 
674     protected Object createDialog(Map properties) {
675         Object owner = properties.remove("owner");
676         if (owner instanceof Frame) {
677             return new JDialog((Frame) owner);
678         }
679         else if (owner instanceof Dialog) {
680             return new JDialog((Dialog) owner);
681         }
682         else {
683             return new JDialog();
684         }
685     }
686 
687     protected Object createComboBox(Map properties) {
688         Object items = properties.remove("items");
689         if (items instanceof Vector) {
690             return new JComboBox((Vector) items);
691         }
692         else if (items instanceof List) {
693             List list = (List) items;
694             return new JComboBox(list.toArray());
695         }
696         else if (items instanceof Object[]) {
697             return new JComboBox((Object[]) items);
698         }
699         else {
700             return new JComboBox();
701         }
702     }
703 
704     protected void registerBeanFactory(String name, final Class beanClass) {
705         registerFactory(name, new Factory() {
706             public Object newInstance(Map properties) throws InstantiationException, IllegalAccessException {
707                 return beanClass.newInstance();
708             }
709         });
710 
711     }
712 
713     protected void registerFactory(String name, Factory factory) {
714         factories.put(name, factory);
715     }
716 }