KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > swixml > SwingEngine


1 /*--
2  $Id: SwingEngine.java,v 1.4 2005/06/01 00:06:23 wolfpaulus Exp $
3
4  Copyright (C) 2003-2005 Wolf Paulus.
5  All rights reserved.
6
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions
9  are met:
10
11  1. Redistributions of source code must retain the above copyright
12  notice, this list of conditions, and the following disclaimer.
13
14  2. Redistributions in binary form must reproduce the above copyright
15  notice, this list of conditions, and the disclaimer that follows
16  these conditions in the documentation and/or other materials provided
17  with the distribution.
18
19  3. The end-user documentation included with the redistribution,
20  if any, must include the following acknowledgment:
21         "This product includes software developed by the
22          SWIXML Project (http://www.swixml.org/)."
23  Alternately, this acknowledgment may appear in the software itself,
24  if and wherever such third-party acknowledgments normally appear.
25
26  4. The name "Swixml" must not be used to endorse or promote products
27  derived from this software without prior written permission. For
28  written permission, please contact <info_AT_swixml_DOT_org>
29
30  5. Products derived from this software may not be called "Swixml",
31  nor may "Swixml" appear in their name, without prior written
32  permission from the Swixml Project Management.
33
34  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37  DISCLAIMED. IN NO EVENT SHALL THE SWIXML PROJECT OR ITS
38  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
39  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
40  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
41  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
42  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
43  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
44  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  SUCH DAMAGE.
46  ====================================================================
47
48  This software consists of voluntary contributions made by many
49  individuals on behalf of the Swixml Project and was originally
50  created by Wolf Paulus <wolf_AT_swixml_DOT_org>. For more information
51  on the Swixml Project, please see <http://www.swixml.org/>.
52 */

53 package org.swixml;
54
55 import org.jdom.Document;
56 import org.jdom.input.SAXBuilder;
57
58 import javax.swing.*;
59 import java.awt.*;
60 import java.awt.event.ActionListener JavaDoc;
61 import java.awt.event.WindowAdapter JavaDoc;
62 import java.awt.event.WindowEvent JavaDoc;
63 import java.awt.event.WindowListener JavaDoc;
64 import java.io.*;
65 import java.lang.reflect.Field JavaDoc;
66 import java.lang.reflect.Modifier JavaDoc;
67 import java.net.URL JavaDoc;
68 import java.security.AccessControlException JavaDoc;
69 import java.util.*;
70 import java.util.List JavaDoc;
71
72 /**
73  * The SwingEngine class is the rendering engine able to convert an XML descriptor into a java.swing UI.
74  * <p/>
75  * <img SRC="doc-files/swixml_1_0.png" ALIGN="center">
76  * </p>
77  * @author <a HREF="mailto:wolf@paulus.com">Wolf Paulus</a>
78  * @version $Revision: 1.4 $
79  */

80 public class SwingEngine {
81   //
82
// Static Constants
83
//
84
/**
85    * Mac OSX identifier in System.getProperty(os.name)
86    */

87   public static final String JavaDoc MAC_OSX_OS_NAME = "mac os x";
88
89   /**
90    * Mac OSX locale variant to localize strings like quit etc.
91    */

92   public static final String JavaDoc MAC_OSX_LOCALE_VARIANT = "mac";
93
94   /**
95    * Debug / Release Mode
96    */

97   public static final boolean DEBUG_MODE = false;
98   /**
99    * XML Error
100    */

101   private static final String JavaDoc XML_ERROR_MSG = "Invalid SwiXML Descriptor.";
102   /**
103    * IO Error Message.
104    */

105   private static final String JavaDoc IO_ERROR_MSG = "Resource could not be found ";
106   /**
107    * Mapping Error Message.
108    */

109   private static final String JavaDoc MAPPING_ERROR_MSG =
110           " could not be mapped to any Object and remained un-initialized.";
111
112   //
113
// Static Member Variables
114
//
115
/**
116    * main frame
117    */

118   private static Frame appFrame;
119   /**
120    * static resource bundle
121    */

122   private static String JavaDoc default_resource_bundle_name = null;
123   /**
124    * static locale
125    */

126   private static Locale default_locale = Locale.getDefault();
127   /**
128    * Check is currently running on a Mac
129    */

130   private static boolean MAC_OSX = false;
131   /**
132    * static Mac OS X Support, set to true to support Mac UI specialties
133    */

134   private static boolean MAC_OSX_SUPPORTED = true;
135
136   //
137
// Static Initializer
138
//
139
/** display the swing release version to system out. */
140   static {
141     System.out.println("SwixML @version@");
142     try {
143       MAC_OSX = System.getProperty("os.name").toLowerCase().startsWith(SwingEngine.MAC_OSX_OS_NAME);
144     } catch (Exception JavaDoc e) {
145       MAC_OSX = false;
146     }
147   }
148
149   //
150
// Member Variables
151
//
152
/**
153    * Swixml Parser.
154    */

155   private Parser parser = new Parser(this);
156   /**
157    * Client object hosting the swingengine, alternative to extending the SwinEngine Class
158    */

159   private Object JavaDoc client;
160   /**
161    * Root Component for the rendered swing UI.
162    */

163   private Container root;
164   /**
165    * Swing object map, contains only those object that were given an id attribute.
166    */

167   private Map idmap = new HashMap();
168   /**
169    * Flattened Swing object tree, contains all object, even the ones without an id.
170    */

171   private Collection components = null;
172   /**
173    * access to taglib to let overwriting class add and remove tags.
174    */

175   private Localizer localizer = new Localizer();
176   //
177
// Private Constants
178
//
179
/**
180    * Classload to load resources
181    */

182   private final TagLibrary taglib = SwingTagLibrary.getInstance();
183   /**
184    * Localizer, setup by parameters found in the xml descriptor.
185    */

186   protected ClassLoader JavaDoc cl = this.getClass().getClassLoader();
187
188   /**
189    * Default ctor for a SwingEngine.
190    */

191   public SwingEngine() {
192     this.client = this;
193     this.setLocale(SwingEngine.default_locale);
194     this.getLocalizer().setResourceBundle(SwingEngine.default_resource_bundle_name);
195
196     try {
197       if (SwingEngine.isMacOSXSupported() && SwingEngine.isMacOSX()) {
198         // Use apple's ScreenMenuBar instead of the MS-Window style
199
// application's own menu bar
200
System.setProperty("com.apple.macos.useScreenMenuBar", "true");
201         System.setProperty("apple.laf.useScreenMenuBar", "true");
202
203         // Don't let the growbox intrude other widgets
204
System.setProperty("apple.awt.showGrowBox", "true");
205         System.setProperty("com.apple.mrj.application.growbox.intrudes", "false");
206       }
207     } catch (AccessControlException JavaDoc e) {
208       // intentionally empty
209
}
210   }
211
212   /**
213    * Constructor to be used if the SwingEngine is not extend but used through object composition.
214    *
215    * @param client <code>Object</code> owner of this instance
216    */

217   public SwingEngine(Object JavaDoc client) {
218     this();
219     this.client = client;
220   }
221
222   /**
223    * Constructs a new SwingEngine, rendering the provided XML into a javax.swing UI
224    *
225    * @param resource <code>String</code>
226    */

227   public SwingEngine(final String JavaDoc resource) {
228     this(SwingEngine.class.getClassLoader(), resource);
229   }
230
231   /**
232    * Constructs a new SwingEngine, rendering the provided XML into a javax.swing UI
233    *
234    * @param resource <code>String</code>
235    * @deprecated
236    */

237   public SwingEngine(ClassLoader JavaDoc cl, final String JavaDoc resource) {
238     this();
239     this.setClassLoader(cl);
240     Reader reader = null;
241     try {
242       InputStream in = cl.getResourceAsStream(resource);
243       if (in == null) {
244         throw new IOException(IO_ERROR_MSG + resource);
245       }
246       reader = new InputStreamReader(in);
247       render(reader);
248     } catch (Exception JavaDoc e) {
249       if (SwingEngine.DEBUG_MODE)
250         System.err.println(e);
251     } finally {
252       try {
253         reader.close();
254       } catch (Exception JavaDoc e) {
255         // intentionally empty
256
}
257     }
258   }
259
260   /**
261    * Gets the parsing of the XML started.
262    *
263    * @param url <code>URL</code> url pointing to an XML descriptor
264    * @return <code>Object</code>- instanced swing object tree root
265    * @throws Exception
266    */

267   public Container render(final URL JavaDoc url) throws Exception JavaDoc {
268     Reader reader = null;
269     Container obj = null;
270     try {
271       InputStream in = url.openStream();
272       if (in == null) {
273         throw new IOException(IO_ERROR_MSG + url.toString());
274       }
275       reader = new InputStreamReader(in);
276       obj = render(reader);
277     } finally {
278       try {
279         reader.close();
280       } catch (Exception JavaDoc ex) {
281         // intentionally empty
282
}
283     }
284     return obj;
285   }
286
287   /**
288    * Gets the parsing of the XML file started.
289    *
290    * @param resource <code>String</code> xml-file path info
291    * @return <code>Object</code>- instanced swing object tree root
292    */

293   public Container render(final String JavaDoc resource) throws Exception JavaDoc {
294     Reader reader = null;
295     Container obj = null;
296     try {
297       InputStream in = cl.getResourceAsStream(resource);
298       if (in == null) {
299         throw new IOException(IO_ERROR_MSG + resource);
300       }
301       reader = new InputStreamReader(in);
302       obj = render(reader);
303     } finally {
304       try {
305         reader.close();
306       } catch (Exception JavaDoc ex) {
307         // intentionally empty
308
}
309     }
310     return obj;
311   }
312
313   /**
314    * Gets the parsing of the XML file started.
315    *
316    * @param xml_file <code>File</code> xml-file
317    * @return <code>Object</code>- instanced swing object tree root
318    */

319   public Container render(final File xml_file) throws Exception JavaDoc {
320     if (xml_file == null) {
321       throw new IOException();
322     }
323     return render(new FileReader(xml_file));
324   }
325
326   /**
327    * Gets the parsing of the XML file started.
328    *
329    * @param xml_reader <code>Reader</code> xml-file path info
330    * @return <code>Object</code>- instanced swing object tree root
331    */

332   public Container render(final Reader xml_reader) throws Exception JavaDoc {
333     if (xml_reader == null) {
334       throw new IOException();
335     }
336     try {
337       return render(new SAXBuilder().build(xml_reader));
338     } catch (org.xml.sax.SAXParseException JavaDoc e) {
339       System.err.println(e);
340     } catch (org.jdom.input.JDOMParseException e) {
341       System.err.println(e);
342     }
343     throw new Exception JavaDoc(SwingEngine.XML_ERROR_MSG);
344   }
345
346   /**
347    * Gets the parsing of the XML file started.
348    *
349    * @param jdoc <code>Document</code> xml gui descritptor
350    * @return <code>Object</code>- instanced swing object tree root
351    */

352   public Container render(final Document jdoc) throws Exception JavaDoc {
353     idmap.clear();
354     try {
355       root = (Container) parser.parse(jdoc);
356     } catch (Exception JavaDoc e) {
357       if (SwingEngine.DEBUG_MODE)
358         System.err.println(e);
359       throw (e);
360     }
361     // reset components collection
362
components = null;
363     // initialize all client fields with UI components by their id
364
mapMembers(client);
365     if (Frame.class.isAssignableFrom(root.getClass())) {
366       SwingEngine.setAppFrame((Frame) root);
367     }
368     return root;
369   }
370
371   /**
372    * Inserts swing object rendered from an XML document into the given container.
373    * <p/>
374    * <pre>
375    * Differently to the render methods, insert does NOT consider the root node of the XML document.
376    * </pre>
377    * <pre>
378    * <b>NOTE:</b><br>insert() does NOT clear() the idmap before rendering.
379    * Therefore, if this SwingEngine's parser was used before, the idmap still
380    * contains (key/value) pairs (id, JComponent obj. references).<br>If insert() is NOT
381    * used to insert in a previously (with this very SwingEngine) rendered UI,
382    * it is highly recommended to clear the idmap:
383    * <br>
384    * <div>
385    * <code>mySwingEngine.getIdMap().clear()</code>
386    * </div>
387    * </pre>
388    *
389    * @param url <code>URL</code> url pointing to an XML descriptor *
390    * @param container <code>Container</code> target, the swing obj, are added to.
391    * @throws Exception
392    */

393   public void insert(final URL JavaDoc url, final Container container)
394           throws Exception JavaDoc {
395     Reader reader = null;
396     try {
397       InputStream in = url.openStream();
398       if (in == null) {
399         throw new IOException(IO_ERROR_MSG + url.toString());
400       }
401       reader = new InputStreamReader(in);
402       insert(reader, container);
403     } finally {
404       try {
405         reader.close();
406       } catch (Exception JavaDoc ex) {
407         // intentionally empty
408
}
409     }
410   }
411
412   /**
413    * Inserts swing objects rendered from an XML reader into the given container.
414    * <p/>
415    * <pre>
416    * Differently to the render methods, insert does NOT consider the root node of the XML document.
417    * </pre>
418    * <pre>
419    * <b>NOTE:</b><br>insert() does NOT clear() the idmap before rendering.
420    * Therefore, if this SwingEngine's parser was used before, the idmap still
421    * contains (key/value) pairs (id, JComponent obj. references).<br>If insert() is NOT
422    * used to insert in a previously (with this very SwingEngine) rendered UI, it is highly
423    * recommended to clear the idmap:
424    * <br>
425    * <div>
426    * <code>mySwingEngine.getIdMap().clear()</code>
427    * </div>
428    * </pre>
429    *
430    * @param reader <code>Reader</code> xml-file path info
431    * @param container <code>Container</code> target, the swing obj, are added to.
432    * @throws Exception
433    */

434   public void insert(final Reader reader, final Container container)
435           throws Exception JavaDoc {
436     if (reader == null) {
437       throw new IOException();
438     }
439     insert(new SAXBuilder().build(reader), container);
440   }
441
442   /**
443    * Inserts swing objects rendered from an XML reader into the given container.
444    * <p/>
445    * <pre>
446    * Differently to the render methods, insert does NOT consider the root node of the XML document.
447    * </pre>
448    * <pre>
449    * <b>NOTE:</b><br>insert() does NOT clear() the idmap before rendering.
450    * Therefore, if this SwingEngine's parser was used before, the idmap still
451    * contains (key/value) pairs (id, JComponent obj. references).<br>
452    * If insert() is NOT used to insert in a previously (with this very SwingEngine)
453    * rendered UI, it is highly recommended to clear the idmap:
454    * <br>
455    * <div>
456    * <code>mySwingEngine.getIdMap().clear()</code>
457    * </div>
458    * </pre>
459    *
460    * @param resource <code>String</code> xml-file path info
461    * @param container <code>Container</code> target, the swing obj, are added to.
462    * @throws Exception
463    */

464   public void insert(final String JavaDoc resource, final Container container)
465           throws Exception JavaDoc {
466     Reader reader = null;
467     try {
468       InputStream in = cl.getResourceAsStream(resource);
469       if (in == null) {
470         throw new IOException(IO_ERROR_MSG + resource);
471       }
472       reader = new InputStreamReader(in);
473       insert(reader, container);
474     } finally {
475       try {
476         reader.close();
477       } catch (Exception JavaDoc ex) {
478         // intentionally empty
479
}
480     }
481   }
482
483   /**
484    * Inserts swing objects rendered from an XML document into the given container.
485    * <p/>
486    * <pre>
487    * Differently to the parse methods, insert does NOT consider the root node of the XML document.
488    * </pre>
489    * <pre>
490    * <b>NOTE:</b><br>insert() does NOT clear() the idmap before rendering.
491    * Therefore, if this SwingEngine's parser was used before, the idmap still
492    * contains (key/value) pairs (id, JComponent obj. references).<br>
493    * If insert() is NOT
494    * used to insert in a previously (with this very SwingEngine) rendered UI,
495    * it is highly recommended to clear the idmap:
496    * <br>
497    * <div>
498    * <code>mySwingEngine.getIdMap().clear()</code>
499    * </div>
500    * </pre>
501    *
502    * @param jdoc <code>Document</code> xml-doc path info
503    * @throws Exception
504    */

505   public void insert(final Document jdoc, final Container container)
506           throws Exception JavaDoc {
507     root = container;
508     try {
509       parser.parse(jdoc, container);
510     } catch (Exception JavaDoc e) {
511       if (SwingEngine.DEBUG_MODE)
512         System.err.println(e);
513       throw (e);
514     }
515     // reset components collection
516
components = null;
517     // initialize all client fields with UI components by their id
518
mapMembers(client);
519   }
520
521   /**
522    * Sets the SwingEngine's global resource bundle name, to be used by all SwingEngine instances. This name can be
523    * overwritten however for a single instance, if a <code>bundle</code> attribute is places in the root tag of an XML
524    * descriptor.
525    *
526    * @param bundlename <code>String</code> the resource bundle name.
527    */

528   public static void setResourceBundleName(String JavaDoc bundlename) {
529     SwingEngine.default_resource_bundle_name = bundlename;
530   }
531
532   /**
533    * Sets the SwingEngine's global locale, to be used by all SwingEngine instances. This locale can be overwritten
534    * however for a single instance, if a <code>locale</code> attribute is places in the root tag of an XML descriptor.
535    *
536    * @param locale <code>Locale</code>
537    */

538   public static void setDefaultLocale(Locale locale) {
539     SwingEngine.default_locale = locale;
540   }
541
542   /**
543    * Sets the SwingEngine's global application frame variable, to be used as a parent for all child dialogs.
544    *
545    * @param frame <code>Object</code> the parent for all future dialogs.
546    */

547   public static void setAppFrame(Frame frame) {
548     if (frame != null) {
549       if (SwingEngine.appFrame == null) {
550         SwingEngine.appFrame = frame;
551       }
552     }
553   }
554
555   /**
556    * @return <code>Frame</code> a parent for all dialogs.
557    */

558   public static Frame getAppFrame() {
559     return SwingEngine.appFrame;
560   }
561
562   /**
563    * Returns the object which instantiated this SwingEngine.
564    *
565    * @return <code>Objecy</code> SwingEngine client object
566    * <p/>
567    * <pre><b>Note:</b><br>
568    * This is the object used through introspection the actions and fileds are set.
569    * </pre>
570    */

571   public Object JavaDoc getClient() {
572     return client;
573   }
574
575   /**
576    * Returns the root component of the generated Swing UI.
577    *
578    * @return <code>Component</code>- the root component of the javax.swing ui
579    */

580   public Container getRootComponent() {
581     return root;
582   }
583
584   /**
585    * Returns an Iterator for all parsed GUI components.
586    *
587    * @return <code>Iterator</code> GUI components itearator
588    */

589   public Iterator getAllComponentItertor() {
590     if (components == null) {
591       traverse(root, components = new ArrayList());
592     }
593     return components.iterator();
594   }
595
596   /**
597    * Returns an Iterator for id-ed parsed GUI components.
598    *
599    * @return <code>Iterator</code> GUI components itearator
600    */

601   public Iterator getIdComponentItertor() {
602     return idmap.values().iterator();
603   }
604
605   /**
606    * Returns the id map, containing all id-ed parsed GUI components.
607    *
608    * @return <code>Map</code> GUI components map
609    */

610   public Map getIdMap() {
611     return idmap;
612   }
613
614   /**
615    * Removes all un-displayable compontents from the id map and deletes the components collection (for recreation at the
616    * next request).
617    * <p/>
618    * <pre>
619    * A component is made undisplayable either when it is removed from a displayable containment hierarchy or when its
620    * containment hierarchy is made undisplayable. A containment hierarchy is made undisplayable when its ancestor
621    * window
622    * is disposed.
623    * </pre>
624    *
625    * @return <code>int</code> number of removed componentes.
626    */

627   public int cleanup() {
628     List JavaDoc zombies = new ArrayList();
629     Iterator it = idmap.keySet().iterator();
630     while (it != null && it.hasNext()) {
631       Object JavaDoc key = it.next();
632       Object JavaDoc obj = idmap.get(key);
633       if (obj instanceof Component && !((Component) obj).isDisplayable()) {
634         zombies.add(key);
635       }
636     }
637     for (int i = 0; i < zombies.size(); i++) {
638       idmap.remove(zombies.get(i));
639     }
640     components = null;
641     return zombies.size();
642   }
643
644   /**
645    * Removes the id from the internal from the id map, to make the given id available for re-use.
646    *
647    * @param id <code>String</code> assigned name
648    */

649   public void forget(final String JavaDoc id) {
650     idmap.remove(id);
651   }
652
653   /**
654    * Returns the UI component with the given name or null.
655    *
656    * @param id <code>String</code> assigned name
657    * @return <code>Component</code>- the GUI component with the given name or null if not found.
658    */

659   public Component find(final String JavaDoc id) {
660     Object JavaDoc obj = idmap.get(id);
661     if (obj != null && !Component.class.isAssignableFrom(obj.getClass())) {
662       obj = null;
663     }
664     return (Component) obj;
665   }
666
667   /**
668    * Sets the locale to be used during parsing / String conversion
669    *
670    * @param l <code>Locale</code>
671    */

672   public void setLocale(Locale l) {
673     if (SwingEngine.isMacOSXSupported() && SwingEngine.isMacOSX()) {
674       l = new Locale(l.getLanguage(),
675               l.getCountry(),
676               SwingEngine.MAC_OSX_LOCALE_VARIANT);
677     }
678     this.localizer.setLocale(l);
679   }
680
681   /**
682    * Sets the ResourceBundle to be used during parsing / String conversion
683    *
684    * @param bundlename <code>String</code>
685    */

686   public void setResourceBundle(String JavaDoc bundlename) {
687     this.localizer.setResourceBundle(bundlename);
688   }
689
690   /**
691    * @return <code>Localizer</code>- the Localizer, which is used for localization.
692    */

693   public Localizer getLocalizer() {
694     return localizer;
695   }
696
697   /**
698    * @return <code>TagLibrary</code>- the Taglibray to insert custom tags.
699    * <p/>
700    * <pre><b>Note:</b>ConverterLibrary and TagLibray need to be set up before rendering is called.
701    * </pre>
702    */

703   public TagLibrary getTaglib() {
704     return taglib;
705   }
706
707   /**
708    * Sets a classloader to be used for all <i>getResourse..()</i> and <i> loadClass()</i> calls. If no class loader is
709    * set, the SwingEngine's loader is used.
710    *
711    * @param cl <code>ClassLoader</code>
712    * @see ClassLoader#loadClass
713    * @see ClassLoader#getResource
714    */

715   public void setClassLoader(ClassLoader JavaDoc cl) {
716     this.cl = cl;
717     this.localizer.setClassLoader(cl);
718   }
719
720   /**
721    * @return <code>ClassLoader</code>- the Classloader used for all <i> getResourse..()</i> and <i>loadClass()</i>
722    * calls.
723    */

724   public ClassLoader JavaDoc getClassLoader() {
725     return cl;
726   }
727
728   /**
729    * Recursively Sets an ActionListener
730    * <p/>
731    * <pre>
732    * Backtracking algorithm: if al was set for a child component, its not being set for its parent
733    * </pre>.
734    *
735    * @param c <code>Component</code> start component
736    * @param al <code>ActionListener</code>
737    */

738   public boolean setActionListener(final Component c, final ActionListener JavaDoc al) {
739     boolean b = false;
740     if (c != null) {
741       if (Container.class.isAssignableFrom(c.getClass())) {
742         final Component[] s = ((Container) c).getComponents();
743         for (int i = 0; i < s.length; i++) {
744           b = b | setActionListener(s[i], al);
745         }
746       }
747       if (!b) {
748         if (JMenu.class.isAssignableFrom(c.getClass())) {
749           final JMenu m = (JMenu) c;
750           final int k = m.getItemCount();
751           for (int i = 0; i < k; i++) {
752             b = b | setActionListener(m.getItem(i), al);
753           }
754         } else if (AbstractButton.class.isAssignableFrom(c.getClass())) {
755           ((AbstractButton) c).addActionListener(al);
756           b = true;
757         }
758       }
759
760     }
761     return b;
762   }
763
764   /**
765    * Walks the whole tree to add all components into the <code>components<code> collection.
766    *
767    * @param c <code> Component</code> recursive start component.
768    * <p>
769    * Note:There is another collection available that only tracks
770    * those object that were provided with an <em>id</em>attribute, which hold an unique id
771    * </p>
772    */

773   public Iterator getDescendants(final Component c) {
774     List JavaDoc list = new ArrayList(12);
775     SwingEngine.traverse(c, list);
776     return list.iterator();
777   }
778
779   /**
780    * Introspects the given object's class and initializes its non-transient fields with objects that have been instanced
781    * during parsing. Mappping happens based on type and field name: the fields name has to be equal to the tag id,
782    * psecified in the XML descriptor. The fields class has to be assignable (equals or super class..) from the class
783    * that was used to instance the tag.
784    *
785    * @param obj <code>Object</code> target object to be mapped with instanced tags
786    */

787   protected void mapMembers(Object JavaDoc obj) {
788     if (obj != null) {
789       mapMembers(obj, obj.getClass());
790     }
791   }
792
793   private void mapMembers(Object JavaDoc obj, Class JavaDoc cls) {
794
795     if (obj != null && cls != null && !Object JavaDoc.class.equals(cls)) {
796       Field JavaDoc[] flds = cls.getDeclaredFields();
797       //
798
// loops through class' declared fields and try to find a matching widget.
799
//
800
for (int i = 0; i < flds.length; i++) {
801         Object JavaDoc widget = idmap.get(flds[i].getName());
802         if (widget != null) {
803           // field and object type need to be compatible and field must not be declared Transient
804
if (flds[i].getType().isAssignableFrom(widget.getClass()) && !Modifier.isTransient(flds[i].getModifiers())) {
805             try {
806               boolean accessible = flds[i].isAccessible();
807               flds[i].setAccessible(true);
808               flds[i].set(obj, widget);
809               flds[i].setAccessible(accessible);
810             } catch (IllegalArgumentException JavaDoc e) {
811               // intentionally empty
812
} catch (IllegalAccessException JavaDoc e) {
813               // intentionally empty
814
}
815           }
816         }
817
818         //
819
// If an intended mapping didn't work out the objects member would remain un-initialized.
820
// To prevent this, we try to instantiate with a default ctor.
821
//
822
if (flds[i] == null) {
823           if (!SwingEngine.DEBUG_MODE) {
824             try {
825               flds[i].set(obj, flds[i].getType().newInstance());
826             } catch (IllegalArgumentException JavaDoc e) {
827               // intentionally empty
828
} catch (IllegalAccessException JavaDoc e) {
829               // intentionally empty
830
} catch (InstantiationException JavaDoc e) {
831               // intentionally empty
832
}
833           } else { // SwingEngine.DEBUG_MODE)
834
System.err.println(flds[i].getType()
835                     + " : "
836                     + flds[i].getName()
837                     + SwingEngine.MAPPING_ERROR_MSG);
838           }
839         }
840       }
841
842       // Since getDeclaredFields() only works on the class itself, not the super class,
843
// we need to make this recursive down to the object.class
844
mapMembers(obj, cls.getSuperclass());
845     }
846   }
847
848   /**
849    * Walks the whole tree to add all components into the <code>components<code> collection.
850    *
851    * @param c <code>Component</code> recursive start component.
852    * <p>
853    * Note:There is another collection available that only tracks
854    * those object that were provided with an <em>id</em>attribute, which hold an unique id
855    * </p>
856    */

857   protected static void traverse(final Component c, Collection collection) {
858     if (c != null) {
859       collection.add(c);
860       if (c instanceof JMenu) {
861         final JMenu m = (JMenu) c;
862         final int k = m.getItemCount();
863         for (int i = 0; i < k; i++) {
864           traverse(m.getItem(i), collection);
865         }
866       } else if (c instanceof Container) {
867         final Component[] s = ((Container) c).getComponents();
868         for (int i = 0; i < s.length; i++) {
869           traverse(s[i], collection);
870         }
871       }
872     }
873   }
874
875   /**
876    * Enables or disables support of Mac OS X GUIs
877    *
878    * @param osx <code>boolean</code>
879    */

880   public static void setMacOSXSuport(boolean osx) {
881     SwingEngine.MAC_OSX_SUPPORTED = osx;
882   }
883
884   /**
885    * Indicates state of Mac OS X support (default is true = ON).
886    *
887    * @return <code>boolean</code>- indicating MacOS support is enabled
888    */

889   public static boolean isMacOSXSupported() {
890     return SwingEngine.MAC_OSX_SUPPORTED;
891   }
892
893   /**
894    * Indicates if currently running on Mac OS X
895    *
896    * @return <code>boolean</code>- indicating if currently running on a MAC
897    */

898   public static boolean isMacOSX() {
899     return SwingEngine.MAC_OSX;
900   }
901
902   /**
903    * Displays the GUI during a RAD session. If the root component is neither a JFrame nor a JDialog, the a JFrame is
904    * instantiated and the root is added into the new frames contentpane.
905    */

906   public void test() {
907     WindowListener JavaDoc wl = new WindowAdapter JavaDoc() {
908       public void windowClosing(WindowEvent JavaDoc e) {
909         super.windowClosing(e);
910         System.exit(0);
911       }
912     };
913     if (root != null) {
914       if (JFrame.class.isAssignableFrom(root.getClass())
915               || JDialog.class.isAssignableFrom(root.getClass())) {
916         ((Window) root).addWindowListener(wl);
917         root.setVisible(true);
918       } else {
919         JFrame jf = new JFrame("SwiXml Test");
920         jf.getContentPane().add(root);
921         jf.pack();
922         jf.addWindowListener(wl);
923         jf.setVisible(true);
924       }
925     }
926   }
927 }
928
Popular Tags