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: 331171 $
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  		Component comp = getComponent();
87          String componentName = (comp!=null) ? comp.getName() : null;
88          if (comp!=null && (componentName == null || componentName.length() == 0))
89              componentName = getComponent().toString();
90          return "ComponentTag with bean " + componentName;
91      }
92  
93      /***
94       * Sets the Action of this component
95       */
96      public void setAction(Action action) throws JellyTagException {
97          Component component = getComponent();
98          if ( component != null ) {
99              // lets just try set the 'action' property
100             try {
101                 BeanUtils.setProperty( component, "action", action );
102             } catch (IllegalAccessException e) {
103                 throw new JellyTagException(e);
104             } catch (InvocationTargetException e) {
105                 throw new JellyTagException(e);
106             }
107         }
108     }
109 
110     /***
111      * Sets the Font of this component
112      */
113     public void setFont(Font font) throws JellyTagException {
114         Component component = getComponent();
115         if ( component != null ) {
116             // lets just try set the 'font' property
117             try {
118                 BeanUtils.setProperty( component, "font", font );
119             }
120             catch (IllegalAccessException e) {
121                 throw new JellyTagException(e);
122             }
123             catch (InvocationTargetException e) {
124                 throw new JellyTagException(e);
125             }
126         }
127     }
128 
129     /***
130      * Sets the Border of this component
131      */
132     public void setBorder(Border border) throws JellyTagException {
133         Component component = getComponent();
134         if ( component != null ) {
135             try {
136                 // lets just try set the 'border' property
137                 BeanUtils.setProperty( component, "border", border );
138             }
139             catch (IllegalAccessException e) {
140                 throw new JellyTagException(e);
141             }
142             catch (InvocationTargetException e) {
143                 throw new JellyTagException(e);
144             }
145         }
146     }
147 
148     /***
149      * Sets the LayoutManager of this component
150      */
151     public void setLayout(LayoutManager layout) throws JellyTagException {
152         Component component = getComponent();
153         if ( component != null ) {
154             if ( component instanceof RootPaneContainer ) {
155                 RootPaneContainer rpc = (RootPaneContainer) component;
156                 component = rpc.getContentPane();
157             }
158 
159             try {
160                 // lets just try set the 'layout' property
161                 BeanUtils.setProperty( component, "layout", layout );
162             }
163             catch (IllegalAccessException e) {
164                 throw new JellyTagException(e);
165             }
166             catch (InvocationTargetException e) {
167                 throw new JellyTagException(e);
168             }
169         }
170     }
171 	
172     
173 	private String tagName = null;
174 	
175 	private XMLOutput currentOutput = null;
176 	
177 	/*** Puts this tag into the context under the given name
178 	 * allowing later calls to {@link #rerun()}.
179 	 * For example, it makes sense to use ${myTag.rerun()} as a child
180 	 * of an <code>action</code> element.
181 	 *
182 	 * @param the name to be used
183 	 */
184 	public void setTagName(String name) {
185 		this.tagName = name;
186 	}
187 
188     /***
189      * Adds a WindowListener to this component
190      */
191     public void addWindowListener(WindowListener listener) throws JellyTagException {
192         Component component = getComponent();
193         if ( component instanceof Window ) {
194             Window window = (Window) component;
195             window.addWindowListener(listener);
196         }
197     }
198 
199     /***
200      * Adds a FocusListener to this component
201      */
202     public void addFocusListener(FocusListener listener) throws JellyTagException {
203         Component component = getComponent();
204         component.addFocusListener(listener);
205     }
206 
207     /***
208      * Adds a KeyListener to this component
209      */
210     public void addKeyListener(KeyListener listener) throws JellyTagException {
211         Component component = getComponent();
212         component.addKeyListener(listener);
213     }
214 
215     // Properties
216     //-------------------------------------------------------------------------
217 
218     /***
219      * @return the visible component, if there is one.
220      */
221     public Component getComponent() {
222         Object bean = getBean();
223         if ( bean instanceof Component ) {
224             return (Component) bean;
225         }
226         return null;
227     }
228 
229 
230     // ContainerTag interface
231     //-------------------------------------------------------------------------
232 
233     /***
234      * Adds a child component to this parent
235      */
236     public void addChild(Component component, Object constraints) throws JellyTagException {
237         Object parent = getBean();
238         if ( parent instanceof JFrame && component instanceof JMenuBar ) {
239             JFrame frame = (JFrame) parent;
240             frame.setJMenuBar( (JMenuBar) component );
241         }
242         else if ( parent instanceof RootPaneContainer ) {
243             RootPaneContainer rpc = (RootPaneContainer) parent;
244             if (constraints != null) {
245                 rpc.getContentPane().add( component, constraints );
246             }
247             else {
248                 rpc.getContentPane().add( component);
249             }
250         }
251         else if ( parent instanceof JScrollPane ) {
252             JScrollPane scrollPane = (JScrollPane) parent;
253             scrollPane.setViewportView( component );
254         }
255         else if ( parent instanceof JSplitPane) {
256             JSplitPane splitPane = (JSplitPane) parent;
257             if ( splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT ) {
258                 if ( splitPane.getTopComponent() == null ) {
259                     splitPane.setTopComponent( component );
260                 }
261                 else {
262                     splitPane.setBottomComponent( component );
263                 }
264             }
265             else {
266                 if ( splitPane.getLeftComponent() == null ) {
267                     splitPane.setLeftComponent( component );
268                 }
269                 else {
270                     splitPane.setRightComponent( component );
271                 }
272             }
273         }
274         else if ( parent instanceof JMenuBar && component instanceof JMenu ) {
275             JMenuBar menuBar = (JMenuBar) parent;
276             menuBar.add( (JMenu) component );
277         }
278         else if ( parent instanceof Container ) {
279             Container container = (Container) parent;
280             if (constraints != null) {
281                 container.add( component, constraints );
282             }
283             else {
284                 container.add( component );
285             }
286         }
287     }
288 
289 
290     // Implementation methods
291     //-------------------------------------------------------------------------
292 
293     /***
294      * A class may be specified otherwise the Factory will be used.
295      */
296     protected Class convertToClass(Object classObject) throws MissingAttributeException, ClassNotFoundException {
297         if (classObject == null) {
298             return null;
299         }
300         else {
301             return super.convertToClass(classObject);
302         }
303     }
304 
305     /***
306      * A class may be specified otherwise the Factory will be used.
307      */
308     protected Object newInstance(Class theClass, Map attributes, XMLOutput output) throws JellyTagException {
309 		if (attributes.containsKey("tagName")) {
310 			this.setTagName((String)attributes.get("tagName"));
311 			addIgnoreProperty("tagName");
312 		}
313 		if(tagName!=null) {
314 			context.setVariable(tagName,this);
315 			currentOutput = output;
316 		}
317         try {
318             if (theClass != null ) {
319                 return theClass.newInstance();
320             } else {
321                 return factory.newInstance();
322             }
323         } catch (IllegalAccessException e) {
324             throw new JellyTagException(e);
325         } catch (InstantiationException e) {
326             throw new JellyTagException(e);
327         }
328     }
329 
330 
331     /***
332      * Either defines a variable or adds the current component to the parent
333      */
334     protected void processBean(String var, Object bean) throws JellyTagException {
335         if (var != null) {
336             context.setVariable(var, bean);
337         }
338         Component component = getComponent();
339         if ( component != null ) {
340             ContainerTag parentTag = (ContainerTag) findAncestorWithClass( ContainerTag.class );
341             if ( parentTag != null ) {
342                 parentTag.addChild(component, getConstraint());
343             }
344             else {
345                 if (var == null) {
346                     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" );
347                 }
348             }
349         }
350     }
351 
352     /***
353      * Handles wierd properties that don't quite match the Java Beans contract
354      */
355     protected void setBeanProperties(Object bean, Map attributes) throws JellyTagException {
356             
357             Component component = getComponent();
358             if (component != null) {
359                 if (attributes.containsKey("location")) {
360                     Object value = attributes.get("location");
361                     Point p = null;
362                     if (value instanceof Point) {
363                         p = (Point) value;
364                     }
365                     else if (value != null) {
366                         p =
367                             (Point) ConvertUtils.convert(
368                                 value.toString(),
369                                 Point.class);
370                     }
371                     component.setLocation(p);
372                     addIgnoreProperty("location");
373                 }
374 
375                 if (attributes.containsKey("size")) {
376                     Object value = attributes.get("size");
377                     Dimension d = null;
378                     if (value instanceof Dimension) {
379                         d = (Dimension) value;
380                     }
381                     else if (value != null) {
382                         d =
383                             (Dimension) ConvertUtils.convert(
384                                 value.toString(),
385                                 Dimension.class);
386                     }
387                     component.setSize(d);
388                     addIgnoreProperty("size");
389                 }
390 				
391                 
392                 if (attributes.containsKey("debugGraphicsOptions")) {
393                     try {
394                         Object o = debugGraphicsConverter.convert(attributes.get("debugGraphicsOptions"));
395                         attributes.put("debugGraphicsOptions", o);
396                     } catch (IllegalArgumentException e) {
397                         throw new JellyTagException(e);
398                     }
399                 }
400                 
401                 if (attributes.containsKey("debugGraphics")) {
402                     try {
403                         Object o = debugGraphicsConverter.convert(attributes.get("debugGraphics"));
404                         attributes.put("debugGraphicsOptions", o);
405                     } catch (IllegalArgumentException e) {
406                         throw new JellyTagException(e);
407                     }
408                     
409                     addIgnoreProperty("debugGraphics");
410                 }
411                 
412              super.setBeanProperties(bean, attributes);
413         }
414     }
415 
416     protected Object getConstraint() {
417         return null;
418     }
419 
420     /***Overrides the default UseBean functionality to clear the bean after the
421      * tag runs. This prevents us from keeping references to heavy Swing objects
422      * around for longer than they are needed.
423      * @see org.apache.commons.jelly.Tag#doTag(org.apache.commons.jelly.XMLOutput)
424      */
425     public void doTag(XMLOutput output) throws JellyTagException {
426         super.doTag(output);
427         clearBean();
428     }
429 
430     /*** Sets the bean to null, to prevent it from
431      * sticking around in the event that this tag instance is
432      * cached. This method is called at the end of doTag.
433      *
434      */
435     protected void clearBean() {
436         setBean(null);
437     }
438 }