001: package abbot.finder;
002:
003: import java.awt.*;
004: import javax.swing.*;
005: import java.util.*;
006:
007: import abbot.Log;
008: import abbot.ExitException;
009: import abbot.tester.WindowTracker;
010: import abbot.tester.Robot;
011: import abbot.util.AWT;
012:
013: /** Provides access to the current AWT hierarchy. */
014: public class AWTHierarchy implements Hierarchy {
015: protected static final WindowTracker tracker = WindowTracker
016: .getTracker();
017: protected static final Collection EMPTY = new ArrayList();
018:
019: private static Hierarchy defaultHierarchy = null;
020:
021: /** Obtain a default Hierarchy. This method is provided only to support
022: * the deprecated <code>ComponentTester.assertFrameShowing()</code> method.
023: */
024: public static Hierarchy getDefault() {
025: /*System.out.println("Using default Hierarchy: "
026: + Log.getStack(Log.FULL_STACK));*/
027: return defaultHierarchy != null ? defaultHierarchy
028: : new AWTHierarchy();
029: }
030:
031: /** Set the default Hierarchy. This method is provided only to support
032: * the deprecated <code>ComponentTester.assertFrameShowing()</code> method.
033: */
034: public static void setDefault(Hierarchy h) {
035: defaultHierarchy = h;
036: }
037:
038: /** Returns whether the given component is reachable from any of the root
039: * windows. The default is to consider all components to be contained in
040: * the hierarchy, whether they are reachable or not (NOTE: isReachable is
041: * a distinctly different operation).
042: */
043: public boolean contains(Component c) {
044: return true;
045: }
046:
047: /** Properly dispose of the given Window, making it and its native
048: * resources available for garbage collection.
049: */
050: public void dispose(final Window w) {
051: if (AWT.isAppletViewerFrame(w)) {
052: // Don't dispose, it must quit on its own
053: return;
054: }
055:
056: Log.debug("Dispose " + w);
057: Window[] owned = w.getOwnedWindows();
058:
059: for (int i = 0; i < owned.length; i++) {
060: // Window.dispose is recursive; make Hierarchy.dispose recursive
061: // as well.
062: dispose(owned[i]);
063: }
064:
065: if (AWT.isSharedInvisibleFrame(w)) {
066: // Don't dispose, or any child windows which may be currently
067: // ignored (but not hidden) will be hidden and disposed.
068: return;
069: }
070:
071: // Ensure the dispose is done on the swing thread so we can catch any
072: // exceptions. If Window.dispose is called from a non-Swing thread,
073: // it will invokes the dispose action on the Swing thread but in that
074: // case we have no control over exceptions.
075: Runnable action = new Runnable() {
076: public void run() {
077: try {
078: // Distinguish between the abbot framework disposing a
079: // window and anyone else doing so.
080: System.setProperty("abbot.finder.disposal", "true");
081: w.dispose();
082: System
083: .setProperty("abbot.finder.disposal",
084: "false");
085: } catch (NullPointerException npe) {
086: // Catch bug in AWT 1.3.1 when generating hierarchy
087: // events
088: Log.log(npe);
089: } catch (ExitException e) {
090: // Some apps might call System.exit on WINDOW_CLOSED
091: Log.log("Ignoring SUT exit: " + e);
092: } catch (Throwable e) {
093: // Don't allow other exceptions to interfere with
094: // disposal.
095: Log.warn(e);
096: Log.warn("An exception was thrown when disposing "
097: + " the window " + Robot.toString(w)
098: + ". The exception is ignored");
099: }
100: }
101: };
102: if (SwingUtilities.isEventDispatchThread()) {
103: action.run();
104: } else {
105: try {
106: SwingUtilities.invokeAndWait(action);
107: } catch (Exception e) {
108: }
109: }
110: }
111:
112: /** Return all root components in the current AWT hierarchy. */
113: public Collection getRoots() {
114: return tracker.getRootWindows();
115: }
116:
117: /** Return all descendents of interest of the given Component.
118: This includes owned windows for Windows, children for Containers.
119: */
120: public Collection getComponents(Component c) {
121: if (c instanceof Container) {
122: Container cont = (Container) c;
123: ArrayList list = new ArrayList();
124: list.addAll(Arrays.asList(cont.getComponents()));
125: // Add other components which are not explicitly children, but
126: // that are conceptually descendents
127: if (c instanceof JMenu) {
128: list.add(((JMenu) c).getPopupMenu());
129: } else if (c instanceof Window) {
130: list.addAll(Arrays.asList(((Window) c)
131: .getOwnedWindows()));
132: } else if (c instanceof JDesktopPane) {
133: // Add iconified frames, which are otherwise unreachable.
134: // For consistency, they are still considerered children of
135: // the desktop pane.
136: list.addAll(findInternalFramesFromIcons(cont));
137: }
138: return list;
139: }
140: return EMPTY;
141: }
142:
143: private Collection findInternalFramesFromIcons(Container cont) {
144: ArrayList list = new ArrayList();
145: int count = cont.getComponentCount();
146: for (int i = 0; i < count; i++) {
147: Component child = cont.getComponent(i);
148: if (child instanceof JInternalFrame.JDesktopIcon) {
149: JInternalFrame frame = ((JInternalFrame.JDesktopIcon) child)
150: .getInternalFrame();
151: if (frame != null)
152: list.add(frame);
153: }
154: // OSX puts icons into a dock; handle icon manager situations here
155: else if (child instanceof Container) {
156: list
157: .addAll(findInternalFramesFromIcons((Container) child));
158: }
159: }
160: return list;
161: }
162:
163: public Container getParent(Component c) {
164: Container p = c.getParent();
165: if (p == null && c instanceof JInternalFrame) {
166: // workaround for bug in JInternalFrame: COMPONENT_HIDDEN is sent
167: // before the desktop icon is set, so
168: // JInternalFrame.getDesktopPane will throw a NPE if called while
169: // dispatching that event. Reported against 1.4.x.
170: JInternalFrame.JDesktopIcon icon = ((JInternalFrame) c)
171: .getDesktopIcon();
172: if (icon != null) {
173: p = icon.getDesktopPane();
174: }
175: // p = ((JInternalFrame)c).getDesktopPane();
176: }
177: return p;
178: }
179: }
|