View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jelly.tags.swing;
17  
18  
19  import java.awt.Component;
20  import java.awt.Container;
21  import java.awt.Dimension;
22  import java.awt.Font;
23  import java.awt.LayoutManager;
24  import java.awt.Point;
25  import java.awt.Window;
26  import java.awt.event.FocusListener;
27  import java.awt.event.KeyListener;
28  import java.awt.event.WindowListener;
29  import java.lang.reflect.InvocationTargetException;
30  import java.util.Map;
31  
32  import javax.swing.Action;
33  import javax.swing.JFrame;
34  import javax.swing.JMenu;
35  import javax.swing.JMenuBar;
36  import javax.swing.JScrollPane;
37  import javax.swing.JSplitPane;
38  import javax.swing.RootPaneContainer;
39  import javax.swing.border.Border;
40  
41  import org.apache.commons.beanutils.BeanUtils;
42  import org.apache.commons.beanutils.ConvertUtils;
43  import org.apache.commons.jelly.JellyTagException;
44  import org.apache.commons.jelly.MissingAttributeException;
45  import org.apache.commons.jelly.XMLOutput;
46  import org.apache.commons.jelly.tags.core.UseBeanTag;
47  import org.apache.commons.jelly.tags.swing.converters.DebugGraphicsConverter;
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  
51  /***
52   * This tag creates a Swing component and adds it to its parent tag, optionally declaring this
53   * component as a variable if the <i>var</i> attribute is specified.</p>
54   *
55   * <p> This tag clears the reference to it's bean after doTag runs.
56   * This means that child tags can access the component (bean) normally
57   * during execution but should not hold a reference to this
58   * tag after their doTag completes.
59   * </p>
60   *
61   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
62   * @version $Revision: 1.7 $
63   */
64  public class ComponentTag extends UseBeanTag implements ContainerTag {
65  
66      /*** The Log to which logging calls will be made. */
67      private static final Log log = LogFactory.getLog(ComponentTag.class);
68      
69      /*** This is a converter that might normally be used through the 
70       * BeanUtils product. However, it only applies to one Component
71       * property and not to all ints, so it's not registered with BeanUtils.
72       */
73      private static final DebugGraphicsConverter debugGraphicsConverter = new DebugGraphicsConverter();
74      
75      /*** the factory of widgets */
76      private Factory factory;
77  
78      public ComponentTag() {
79      }
80  
81      public ComponentTag(Factory factory) {
82          this.factory = factory;
83      }
84  
85      public String toString() {
86          String componentName = getComponent().getName();
87          if (componentName == null || componentName.length() == 0)
88              componentName = getComponent().toString();
89          return "ComponentTag with bean " + componentName;
90      }
91  
92      /***
93       * Sets the Action of this component
94       */
95      public void setAction(Action action) throws JellyTagException {
96          Component component = getComponent();
97          if ( component != null ) {
98              // lets just try set the 'action' property
99              try {
100                 BeanUtils.setProperty( component, "action", action );
101             } catch (IllegalAccessException e) {
102                 throw new JellyTagException(e);
103             } catch (InvocationTargetException e) {
104                 throw new JellyTagException(e);
105             }
106         }
107     }
108 
109     /***
110      * Sets the Font of this component
111      */
112     public void setFont(Font font) throws JellyTagException {
113         Component component = getComponent();
114         if ( component != null ) {
115             // lets just try set the 'font' property
116             try {
117                 BeanUtils.setProperty( component, "font", font );
118             }
119             catch (IllegalAccessException e) {
120                 throw new JellyTagException(e);
121             }
122             catch (InvocationTargetException e) {
123                 throw new JellyTagException(e);
124             }
125         }
126     }
127 
128     /***
129      * Sets the Border of this component
130      */
131     public void setBorder(Border border) throws JellyTagException {
132         Component component = getComponent();
133         if ( component != null ) {
134             try {
135                 // lets just try set the 'border' property
136                 BeanUtils.setProperty( component, "border", border );
137             }
138             catch (IllegalAccessException e) {
139                 throw new JellyTagException(e);
140             }
141             catch (InvocationTargetException e) {
142                 throw new JellyTagException(e);
143             }
144         }
145     }
146 
147     /***
148      * Sets the LayoutManager of this component
149      */
150     public void setLayout(LayoutManager layout) throws JellyTagException {
151         Component component = getComponent();
152         if ( component != null ) {
153             if ( component instanceof RootPaneContainer ) {
154                 RootPaneContainer rpc = (RootPaneContainer) component;
155                 component = rpc.getContentPane();
156             }
157 
158             try {
159                 // lets just try set the 'layout' property
160                 BeanUtils.setProperty( component, "layout", layout );
161             }
162             catch (IllegalAccessException e) {
163                 throw new JellyTagException(e);
164             }
165             catch (InvocationTargetException e) {
166                 throw new JellyTagException(e);
167             }
168         }
169     }
170 
171     /***
172      * Adds a WindowListener to this component
173      */
174     public void addWindowListener(WindowListener listener) throws JellyTagException {
175         Component component = getComponent();
176         if ( component instanceof Window ) {
177             Window window = (Window) component;
178             window.addWindowListener(listener);
179         }
180     }
181 
182     /***
183      * Adds a FocusListener to this component
184      */
185     public void addFocusListener(FocusListener listener) throws JellyTagException {
186         Component component = getComponent();
187         component.addFocusListener(listener);
188     }
189 
190     /***
191      * Adds a KeyListener to this component
192      */
193     public void addKeyListener(KeyListener listener) throws JellyTagException {
194         Component component = getComponent();
195         component.addKeyListener(listener);
196     }
197 
198     // Properties
199     //-------------------------------------------------------------------------
200 
201     /***
202      * @return the visible component, if there is one.
203      */
204     public Component getComponent() {
205         Object bean = getBean();
206         if ( bean instanceof Component ) {
207             return (Component) bean;
208         }
209         return null;
210     }
211 
212 
213     // ContainerTag interface
214     //-------------------------------------------------------------------------
215 
216     /***
217      * Adds a child component to this parent
218      */
219     public void addChild(Component component, Object constraints) throws JellyTagException {
220         Object parent = getBean();
221         if ( parent instanceof JFrame && component instanceof JMenuBar ) {
222             JFrame frame = (JFrame) parent;
223             frame.setJMenuBar( (JMenuBar) component );
224         }
225         else if ( parent instanceof RootPaneContainer ) {
226             RootPaneContainer rpc = (RootPaneContainer) parent;
227             if (constraints != null) {
228                 rpc.getContentPane().add( component, constraints );
229             }
230             else {
231                 rpc.getContentPane().add( component);
232             }
233         }
234         else if ( parent instanceof JScrollPane ) {
235             JScrollPane scrollPane = (JScrollPane) parent;
236             scrollPane.setViewportView( component );
237         }
238         else if ( parent instanceof JSplitPane) {
239             JSplitPane splitPane = (JSplitPane) parent;
240             if ( splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ) {
241                 if ( splitPane.getTopComponent() == null ) {
242                     splitPane.setTopComponent( component );
243                 }
244                 else {
245                     splitPane.setBottomComponent( component );
246                 }
247             }
248             else {
249                 if ( splitPane.getLeftComponent() == null ) {
250                     splitPane.setLeftComponent( component );
251                 }
252                 else {
253                     splitPane.setRightComponent( component );
254                 }
255             }
256         }
257         else if ( parent instanceof JMenuBar && component instanceof JMenu ) {
258             JMenuBar menuBar = (JMenuBar) parent;
259             menuBar.add( (JMenu) component );
260         }
261         else if ( parent instanceof Container ) {
262             Container container = (Container) parent;
263             if (constraints != null) {
264                 container.add( component, constraints );
265             }
266             else {
267                 container.add( component );
268             }
269         }
270     }
271 
272 
273     // Implementation methods
274     //-------------------------------------------------------------------------
275 
276     /***
277      * A class may be specified otherwise the Factory will be used.
278      */
279     protected Class convertToClass(Object classObject) throws MissingAttributeException, ClassNotFoundException {
280         if (classObject == null) {
281             return null;
282         }
283         else {
284             return super.convertToClass(classObject);
285         }
286     }
287 
288     /***
289      * A class may be specified otherwise the Factory will be used.
290      */
291     protected Object newInstance(Class theClass, Map attributes, XMLOutput output) throws JellyTagException {
292         try {
293             if (theClass != null ) {
294                 return theClass.newInstance();
295             } else {
296                 return factory.newInstance();
297             }
298         } catch (IllegalAccessException e) {
299             throw new JellyTagException(e);
300         } catch (InstantiationException e) {
301             throw new JellyTagException(e);
302         }
303     }
304 
305 
306     /***
307      * Either defines a variable or adds the current component to the parent
308      */
309     protected void processBean(String var, Object bean) throws JellyTagException {
310         if (var != null) {
311             context.setVariable(var, bean);
312         }
313         Component component = getComponent();
314         if ( component != null ) {
315             ContainerTag parentTag = (ContainerTag) findAncestorWithClass( ContainerTag.class );
316             if ( parentTag != null ) {
317                 parentTag.addChild(component, getConstraint());
318             }
319             else {
320                 if (var == null) {
321                     throw new JellyTagException( "The 'var' attribute must be specified or this tag must be nested inside a JellySwing container tag like a widget or a layout" );
322                 }
323             }
324         }
325     }
326 
327     /***
328      * Handles wierd properties that don't quite match the Java Beans contract
329      */
330     protected void setBeanProperties(Object bean, Map attributes) throws JellyTagException {
331             
332             Component component = getComponent();
333             if (component != null) {
334                 if (attributes.containsKey("location")) {
335                     Object value = attributes.get("location");
336                     Point p = null;
337                     if (value instanceof Point) {
338                         p = (Point) value;
339                     }
340                     else if (value != null) {
341                         p =
342                             (Point) ConvertUtils.convert(
343                                 value.toString(),
344                                 Point.class);
345                     }
346                     component.setLocation(p);
347                     addIgnoreProperty("location");
348                 }
349 
350                 if (attributes.containsKey("size")) {
351                     Object value = attributes.get("size");
352                     Dimension d = null;
353                     if (value instanceof Dimension) {
354                         d = (Dimension) value;
355                     }
356                     else if (value != null) {
357                         d =
358                             (Dimension) ConvertUtils.convert(
359                                 value.toString(),
360                                 Dimension.class);
361                     }
362                     component.setSize(d);
363                     addIgnoreProperty("size");
364                 }
365                 
366                 if (attributes.containsKey("debugGraphicsOptions")) {
367                     try {
368                         Object o = debugGraphicsConverter.convert(attributes.get("debugGraphicsOptions"));
369                         attributes.put("debugGraphicsOptions", o);
370                     } catch (IllegalArgumentException e) {
371                         throw new JellyTagException(e);
372                     }
373                 }
374                 
375                 if (attributes.containsKey("debugGraphics")) {
376                     try {
377                         Object o = debugGraphicsConverter.convert(attributes.get("debugGraphics"));
378                         attributes.put("debugGraphicsOptions", o);
379                     } catch (IllegalArgumentException e) {
380                         throw new JellyTagException(e);
381                     }
382                     
383                     addIgnoreProperty("debugGraphics");
384                 }
385                 
386              super.setBeanProperties(bean, attributes);
387         }
388     }
389 
390     protected Object getConstraint() {
391         return null;
392     }
393 
394     /***Overrides the default UseBean functionality to clear the bean after the
395      * tag runs. This prevents us from keeping references to heavy Swing objects
396      * around for longer than they are needed.
397      * @see org.apache.commons.jelly.Tag#doTag(org.apache.commons.jelly.XMLOutput)
398      */
399     public void doTag(XMLOutput output) throws JellyTagException {
400         super.doTag(output);
401         clearBean();
402     }
403 
404     /*** Sets the bean to null, to prevent it from
405      * sticking around in the event that this tag instance is
406      * cached. This method is called at the end of doTag.
407      *
408      */
409     protected void clearBean() {
410         setBean(null);
411     }
412 }