1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
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
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
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
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
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 }