/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.rc.swing.components; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; import java.awt.EventQueue; import java.awt.Window; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Vector; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JToolBar; import javax.swing.SwingUtilities; import org.apache.commons.lang.Validate; import org.eclipse.jubula.rc.common.AUTServer; import org.eclipse.jubula.rc.common.AUTServerConfiguration; import org.eclipse.jubula.rc.common.Constants; import org.eclipse.jubula.rc.common.components.AUTComponent; import org.eclipse.jubula.rc.common.components.AUTHierarchy; import org.eclipse.jubula.rc.common.components.HierarchyContainer; import org.eclipse.jubula.rc.common.exception.ComponentNotManagedException; import org.eclipse.jubula.rc.common.logger.AutServerLogger; import org.eclipse.jubula.rc.swing.SwingAUTServer; import org.eclipse.jubula.rc.swing.listener.ComponentHandler; import org.eclipse.jubula.rc.swing.utils.WorkerRunnable; import org.eclipse.jubula.tools.internal.exception.InvalidDataException; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.eclipse.jubula.tools.internal.objects.ComponentIdentifier; import org.eclipse.jubula.tools.internal.objects.IComponentIdentifier; import org.eclipse.jubula.tools.internal.objects.MappingConstants; import org.eclipse.jubula.tools.internal.utils.EnvironmentUtils; /** * This class holds a hierarchy of the components of the AUT. <br> * * The hierarchy is composed with <code>HierarchyContainer</code>s. For every * component from the AUT a hierarchy container is created. The names for the * components are stored in the appropriate hierarchy containers, instead of the * components itself. Thus the AUTServer does not affect the instances from the * AUT. <br> * * In JRE 1.3 the WINDOW_CLOSED event is not delivered properly, so a window * listener is added to any opened window listening to * <code>WindowEvent.WINDOW_CLOSED</code>.<br> * <p> * <b>Interferences with the AUT</b> * <ul> * <li>The AUTHierarchy is registered as a container listener to every * container from the AUT (but not to the hierarchy container).</li> * <li>The AUTHierarchy is registered as a window listener to every window * from the AUT.</li> * </ul> * * @author BREDEX GmbH * @created 30.08.2004 * */ public class AUTSwingHierarchy extends AUTHierarchy<Component> implements ContainerListener, ComponentListener { /** * name of environment variable / Java property that should be set to * "true" (case-insensitive) if Swing/AWT listeners should be * (de-)registered directly in the thread that handles Swing / AWT events */ private static final String ENV_VAR_SYNC_REGISTER_LISTENERS = "JB_SYNC_REG_SWING_LISTENERS"; //$NON-NLS-1$ /** the logger */ private static AutServerLogger log = new AutServerLogger( AUTSwingHierarchy.class); /** businessprocess for getting components */ private static FindSwingComponentBP findBP = new FindSwingComponentBP(); /** * the worker responsible for handling (de-)registration of * Swing/AWT listeners */ private WorkerRunnable m_listenerRegistrationWorker = new WorkerRunnable(); /** * whether Swing/AWT listeners should be (de-)registered directly in the * thread that handles Swing / AWT events. if not, then a worker thread is * used. */ private boolean m_syncListenerRegistration = false; /** * Constructor */ public AUTSwingHierarchy() { String syncListenersRegistrationValue = EnvironmentUtils .getProcessOrSystemProperty(ENV_VAR_SYNC_REGISTER_LISTENERS); m_syncListenerRegistration = Boolean.valueOf( syncListenersRegistrationValue).booleanValue(); if (!m_syncListenerRegistration) { Thread registrationThread = new Thread(m_listenerRegistrationWorker, "Jubula Listener Registration"); //$NON-NLS-1$ registrationThread.setDaemon(true); registrationThread.start(); } } /** * Adds the complete hierarchy of the given <code>window</code> to the * hierarchy. <br> * @param window a new (and opened) window */ public void add(Window window) { // if window has no parent, its a new top level container, otherwise // the parent is already in the AutHierarchy, // NO!: creating a Window without a parent, calling show() // -> window.getParent() == SwingUtilities$1 // don't add, if in hierarchy map yet if (getRealMap().get(window) == null || getHierarchyContainer(window) == null) { if (log.isInfoEnabled()) { log.info("adding window " + window); //$NON-NLS-1$ } // create a new SwingHierarchyContainer for window AUTComponent<Component> componentID = new SwingComponent(window); HierarchyContainer<Component> hierarchyWindow = new SwingHierarchyContainer(componentID); // update the hash table addToHierachyMap(hierarchyWindow); // add a window listener for window closed events registerAsWindowListener(window); // get the parent of window, if any Container parent = window.getParent(); if (parent != null) { HierarchyContainer<Component> hierarchyParent = getHierarchyContainer(parent); if (hierarchyParent == null) { // a new container, see comment at top of the method hierarchyParent = new SwingHierarchyContainer( new SwingComponent(parent)); } name(hierarchyParent); // add the new container for the window to hierarchyParent hierarchyParent.add(hierarchyWindow); hierarchyWindow.setPrnt(hierarchyParent); name(hierarchyWindow); // update m_hierarchyMap addToHierachyMap(hierarchyParent); addToHierarchyUp(hierarchyParent, parent); } } // registering this class as a container listener happens in addToHierarchy addToHierarchyDown(getHierarchyContainer(window), window); } /** * Removes the given window from the hierarchy. * @param window the window to remove. */ private void remove(Window window) { // remove window from the map // remove the container from hierarchyMap // deregistering the listener from the window happens in window listener itself if (getRealMap().get(window) != null) { HierarchyContainer<Component> windowContainer = getHierarchyMap() .get(getRealMap().get(window)); if (windowContainer != null) { // remove the windowContainer from its parent in the hierarchy, if any HierarchyContainer<Component> parentContainer = windowContainer.getPrnt(); if (parentContainer != null) { parentContainer.remove(windowContainer); } // Remove recursively all hierarchy container from the maps and // remove all listener from the container of the AUT. If the window // is displayed again, the complete hierarchy is rebuild. removeFromHierarchy(windowContainer); } else { // window is not in the hierarchy map // -> log this as an error log.error("an unmanaged window was closed: " + window); //$NON-NLS-1$ } } } /** * Investigates the given <code>component</code> for an identifier. To * obtain this identifier the name of the component and the container * hierarchy is used. * @param component the component to create an identifier for, must not be null. * @throws ComponentNotManagedException if component is null or <br> * (one of the) component(s) in the hierarchy is not managed * @return the identifier for <code>component</code> */ public IComponentIdentifier getComponentIdentifier( Component component) throws ComponentNotManagedException { checkDispatchThread(); IComponentIdentifier result = new ComponentIdentifier(); try { // fill the componentIdentifier result.setComponentClassName(component.getClass().getName()); result.setSupportedClassName(AUTServerConfiguration.getInstance() .getTestableClass(component.getClass()).getName()); List<String> hierarchy = getPathToRoot(component); result.setHierarchyNames(hierarchy); result.setNeighbours(getComponentContext(component)); HierarchyContainer<Component> container = getHierarchyContainer(component); setAlternativeDisplayName(container, component, result); if (component.equals(findBP.findComponent(result, ComponentHandler.getAutHierarchy()))) { result.setEqualOriginalFound(true); } return result; } catch (IllegalArgumentException iae) { // from getPathToRoot() log.error(iae); throw new ComponentNotManagedException( "getComponentIdentifier() called for an unmanaged component: " //$NON-NLS-1$ + component, MessageIDs.E_COMPONENT_NOT_MANAGED); // let pass the ComponentNotManagedException from getPathToRoot() } } /** * {@inheritDoc} */ protected List<String> getComponentContext(Component comp) { List<String> context = new ArrayList<String>(); if (comp.getParent() != null) { HierarchyContainer<Component> parent = getHierarchyContainer( comp.getParent()); if (parent != null) { HierarchyContainer<Component>[] comps = parent.getComps(); for (int i = 0; i < comps.length; i++) { Component child = comps[i].getCompID() .getComponent(); if (!child.equals(comp)) { String toAdd = child.getClass().getName() + Constants.CLASS_NUMBER_SEPERATOR + 1; while (context.contains(toAdd)) { int lastCount = Integer.valueOf( toAdd.substring(toAdd.lastIndexOf( Constants.CLASS_NUMBER_SEPERATOR) + 1)). intValue(); toAdd = child.getClass().getName() + Constants.CLASS_NUMBER_SEPERATOR + (lastCount + 1); } context.add(toAdd); } } } } return context; } /** * {@inheritDoc} */ public IComponentIdentifier[] getAllComponentId() { checkDispatchThread(); List<IComponentIdentifier> result = new Vector<IComponentIdentifier>(); Set<? extends AUTComponent<Component>> keys = getHierarchyMap().keySet(); for (Iterator<? extends AUTComponent<Component>> iter = keys.iterator(); iter.hasNext();) { Component component = iter.next().getComponent(); try { if (AUTServerConfiguration.getInstance().isSupported( component)) { result.add(getComponentIdentifier(component)); } } catch (IllegalArgumentException iae) { // from isSupported -> log log.error("hierarchy map contains null values", iae); //$NON-NLS-1$ // and continue } catch (ComponentNotManagedException e) { // from isSupported -> log log.error("component '" + component.getName() + "' not found!", e); //$NON-NLS-1$ //$NON-NLS-2$ // and continue } } return result.toArray(new IComponentIdentifier[result.size()]); } /** * Search for the component in the AUT with the given * <code>componentIdentifier</code>. * @param componentIdentifier the identifier created in object mapping mode * @throws IllegalArgumentException if the given identifier is null or <br>the hierarchy is not valid: empty or containing null elements * @throws InvalidDataException if the hierarchy in the componentIdentifier does not consist of strings * @throws ComponentNotManagedException if no component could be found for the identifier * @return the instance of the component of the AUT */ public Component findComponent( IComponentIdentifier componentIdentifier) throws IllegalArgumentException, ComponentNotManagedException, InvalidDataException { Component comp = (Component)findBP.findComponent(componentIdentifier, ComponentHandler.getAutHierarchy()); if (comp != null && comp.isShowing()) { Window window = SwingUtilities.getWindowAncestor(comp); if (window != null && window.isShowing() && !window.isActive()) { window.toFront(); } return comp; } throw new ComponentNotManagedException( "unmanaged component with identifier: '" //$NON-NLS-1$ + componentIdentifier.toString() + "'.", //$NON-NLS-1$ MessageIDs.E_COMPONENT_NOT_MANAGED); } /** * @param comp the component to check, whether it's disappeared or not * @return true, if the component disappeared */ public boolean isComponentInHierarchy(Component comp) { if (comp == null) { return false; } return getHierarchyContainer(comp) != null; } /** * Returns the path from the given component to root. The List contains * Strings (the name of the components). * @param component the component to start, it's an instance from the AUT, must not be null * @throws IllegalArgumentException if component is null * @throws ComponentNotManagedException if no hierarchy container exists for the component * @return the path to root, the first elements contains the root, the last element contains the component itself. */ public List<String> getPathToRoot(Component component) throws IllegalArgumentException, ComponentNotManagedException { if (log.isInfoEnabled()) { log.info("pathToRoot called for " + component); //$NON-NLS-1$ } Validate.notNull(component, "The component must not be null"); //$NON-NLS-1$ List<String> hierarchy = new ArrayList<String>(); HierarchyContainer<Component> parent; HierarchyContainer<Component> autContainer = getHierarchyContainer(component); if (autContainer != null) { // add the name of the container itself hierarchy.add(autContainer.getName()); final String className = component.getClass().getName(); if (MappingConstants.SWING_APPLICATION_CLASSNAME.equals(className) || MappingConstants.SWING_MENU_DEFAULT_MAPPING_CLASSNAME .equals(className) || MappingConstants.SWING_MENU_CLASSNAME.equals(className)) { return hierarchy; } parent = getHierarchyContainer(component.getParent()); autContainer.setPrnt(parent); // prepend the name of the container up to the root container while (parent != null) { ((ArrayList<String>)hierarchy).add(0, parent.getName()); Component compo = parent.getCompID().getComponent(); parent = parent.getPrnt(); if (parent == null && compo != null && compo.getParent() != null) { AUTComponent<Component> comp = new SwingComponent(compo.getParent()); HierarchyContainer<Component> container = new SwingHierarchyContainer(comp); name(container); parent = container; addToHierachyMap(container); } } } else { log.error("component '" + component //$NON-NLS-1$ + "' is not managed by this hierarchy"); //$NON-NLS-1$ throw new ComponentNotManagedException( "unmanaged component " + component.toString(), //$NON-NLS-1$ MessageIDs.E_COMPONENT_NOT_MANAGED); } return hierarchy; } /** * Add the new component to the hierarchy. * {@inheritDoc} */ public void componentAdded(ContainerEvent event) { checkDispatchThread(); addComponent(event.getChild()); } /** * Add the new component to the hierarchy. * * @param toAdd The component to add to the hierarchy. */ private void addComponent(Component toAdd) { ClassLoader originalCL = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(this.getClass() .getClassLoader()); try { // Don't add an invisible component if (!toAdd.isShowing()) { return; } Container container = toAdd.getParent(); if (log.isDebugEnabled()) { log.debug("component '" + toAdd //$NON-NLS-1$ + "' added to '" + container + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } // get the hierarchy container for container, must be there! HierarchyContainer<Component> hierarchyContainer = null; if (toAdd instanceof Window) { hierarchyContainer = getHierarchyContainer(toAdd); } else { hierarchyContainer = getHierarchyContainer(container); } if (hierarchyContainer == null) { // Parent container not managed at this time. // Do not clutter up the hierarchy with orphan components. return; } // create new hierarchy container for child, name, update hashtable, put // them together, if (getHierarchyContainer(toAdd) != null) { return; } // create new hierarchy container for child, name, update hashtable, put // them together, HierarchyContainer<Component> hierarchyChild = new SwingHierarchyContainer(new SwingComponent(toAdd)); hierarchyContainer.add(hierarchyChild); hierarchyChild.setPrnt(hierarchyContainer); name(hierarchyChild); addToHierachyMap(hierarchyChild); if (toAdd instanceof Container) { Container cont = (Container)toAdd; // call addTohierachyDown() addToHierarchyDown(hierarchyChild, cont); } } finally { Thread.currentThread().setContextClassLoader(originalCL); } } /** * Remove the removed component from the hierarchy. * {@inheritDoc} */ public void componentRemoved(ContainerEvent event) { checkDispatchThread(); removeComponent(event.getChild(), event.getContainer()); } /** * Remove the removed component from the hierarchy. * * @param toRemove The component to remove from the hierarchy. * @param parent The parent of the component to remove. */ private void removeComponent(Component toRemove, Container parent) { ClassLoader originalCL = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader()); try { if (log.isDebugEnabled()) { log.debug("component '" + toRemove //$NON-NLS-1$ + "' removed from '" + parent + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } // deregister as listener if (toRemove instanceof Container) { deregisterAsContainerListener((Container)toRemove); } HierarchyContainer<Component> hierarchyChild = getHierarchyContainer(toRemove); // update the hierarchy if (hierarchyChild != null) { HierarchyContainer<Component> hierarchyParent = hierarchyChild .getPrnt(); if (hierarchyParent != null) { hierarchyParent.remove(hierarchyChild); } else { // child was not in the hierarchy // -> log this is an error log.error("hierarchy structure corrupted, " //$NON-NLS-1$ + "child has no parent: " + hierarchyChild); //$NON-NLS-1$ } // remove children recursively removeFromHierarchy(hierarchyChild); } else { // child was not in the hierarchy map // -> log this as an error log.debug("an unmanaged component was removed: " + toRemove); //$NON-NLS-1$ } } finally { Thread.currentThread().setContextClassLoader(originalCL); } } /** * Register the AutHierarchy as a container listener to <code>container</code>. * @param container the container to register to */ private void registerAsContainerListener(final Container container) { Runnable registrationRunnable = new Runnable() { public void run() { if (log.isInfoEnabled()) { log.info("registering as listener to container " + container); //$NON-NLS-1$ } ContainerListener[] listener = container.getContainerListeners(); for (int i = 0; i < listener.length; i++) { if (listener[i] instanceof AUTSwingHierarchy) { return; } } container.addContainerListener(AUTSwingHierarchy.this); } }; registerListener(registrationRunnable); } /** * (De-)Registers a Swing / AWT listener, using the given {@link Runnable}. * A worker thread may be used in order to perform the (de-)registration * asynchronously, depending on how the receiver is configured. * * @param registrationRunnable The work to perform in order to * (de-)register the listener. */ private void registerListener(Runnable registrationRunnable) { if (m_syncListenerRegistration) { registrationRunnable.run(); } else { m_listenerRegistrationWorker.addWork(registrationRunnable); } } /** * remove the AutHierarchy as a container listener from <code>container</code>. * @param container the container to deregister from */ private void deregisterAsContainerListener(final Container container) { Runnable deregistrationRunnable = new Runnable() { public void run() { if (log.isInfoEnabled()) { log.info("deregistering as listener from container " + container); //$NON-NLS-1$ } container.removeContainerListener(AUTSwingHierarchy.this); } }; registerListener(deregistrationRunnable); } /** * register a window listener to <code>window</code>.<br> deregistering happens in * <code>WindowClosingListener.windowClosed()</code>. * @param window the window to register to */ private void registerAsWindowListener(final Window window) { Runnable registrationRunnable = new Runnable() { public void run() { if (log.isInfoEnabled()) { log.info("registering window listener to window " //$NON-NLS-1$ + window); } WindowListener[] listener = window.getWindowListeners(); for (int i = 0; i < listener.length; i++) { if (listener[i] instanceof WindowClosingListener) { return; } } window.addWindowListener(new WindowClosingListener()); } }; registerListener(registrationRunnable); } /** * Adds the parent(s) of the given container to the hierarchy recursively. <br> * Recursion stops if the top level container is reached or a parent * container is already known. * * @param hierarchyContainer * the responding SwingHierarchyContainer of container * @param container * the container from the AUT */ private void addToHierarchyUp( HierarchyContainer<Component> hierarchyContainer, Container container) { checkDispatchThread(); if (log.isInfoEnabled()) { log.info("addToHierarchyUp: " //$NON-NLS-1$ + hierarchyContainer + "," + container); //$NON-NLS-1$ } registerAsContainerListener(container); Container parent = container.getParent(); if (parent != null) { // root not reached HierarchyContainer<Component> hierarchyParent = getHierarchyContainer(container); if (hierarchyParent == null) { // unknown SwingHierarchyContainer for parent: // create new hierarchy container, name it, // add current SwingHierarchyContainer to parent hierarchy, // update map m_hierarchyMap // register listener // recursion hierarchyParent = new SwingHierarchyContainer( new SwingComponent(parent)); hierarchyParent.add(hierarchyContainer); hierarchyContainer.setPrnt(hierarchyParent); name(hierarchyParent); addToHierachyMap(hierarchyParent); addToHierarchyUp(hierarchyParent, parent); } } } /** * adds the children of the given container to the hierarchy. * @param hierarchyContainer the responding container (meta data) * @param container the container from the AUT, which children are to be added */ private void addToHierarchyDown( HierarchyContainer<Component> hierarchyContainer, Container container) { checkDispatchThread(); if (log.isInfoEnabled()) { log.info("addToHierarchyDown: " + hierarchyContainer + "," + container); //$NON-NLS-1$ //$NON-NLS-2$ } registerAsContainerListener(container); Collection<Component> collection = getComponents(container); for (Component component : collection) { // Don't add if the component is already in our hierarchy or // if it is invisible. if (getHierarchyContainer(component) != null || !component.isShowing()) { continue; } if (component instanceof Window) { add((Window)component); } else { // add the container HierarchyContainer<Component> newHierarchyContainer = new SwingHierarchyContainer(new SwingComponent(component)); name(newHierarchyContainer); // update the hash table newHierarchyContainer.setPrnt(hierarchyContainer); hierarchyContainer.add(newHierarchyContainer); addToHierachyMap(newHierarchyContainer); if (component instanceof Container) { // recursively down addToHierarchyDown(newHierarchyContainer, (Container)component); } } } name(hierarchyContainer); } /** * removes recursively all containers from <code>container</code><br> * <p> deregisters this from the container from AUT. <br> updates also the internal hierarchy map. * @param container the container to start */ private void removeFromHierarchy(HierarchyContainer<Component> container) { if (container == null) { return; } AUTComponent<Component> autCompID = container.getCompID(); Component autComp = autCompID.getComponent(); if (autComp == null) { log.error("invalid component for removal:" //$NON-NLS-1$ + autCompID.toString()); } removeFromHierachyMap(container); if (autComp instanceof Container) { deregisterAsContainerListener((Container)autComp); } Collection<Component> childs = getComponents(autComp); for (Iterator<Component> iter = childs.iterator(); iter.hasNext();) { removeFromHierarchy(getHierarchyContainer(iter.next())); } } /** * Returns the hierarchy container for <code>component</code>. * @param component the component from the AUT, must no be null * @throws IllegalArgumentException if component is null * @return the hierarchy container or null if the component is not yet managed */ public HierarchyContainer<Component> getHierarchyContainer( Component component) throws IllegalArgumentException { Validate.notNull(component, "The component must not be null"); //$NON-NLS-1$ HierarchyContainer<Component> result = null; try { AUTComponent<Component> compID = getRealMap().get(component); if (compID != null) { result = getHierarchyMap().get(compID); } } catch (ClassCastException cce) { log.error(cce); } catch (NullPointerException npe) { log.error(npe); } return result; } /** * Names the given hierarchy container. <br> * If the managed component has a unique name, this name is used. Otherwise * a name (unique for the hierarchy level) is created. * @param hierarchyContainer the SwingHierarchyContainer to name, if SwingHierarchyContainer is null, * no action is performed and no exception is thrown. */ private void name(HierarchyContainer<Component> hierarchyContainer) { checkDispatchThread(); if (hierarchyContainer != null) { final Component component = hierarchyContainer.getCompID() .getComponent(); String compName = component.getName(); // SPECIAL HANDLING !!! ----------------------------------- if (component instanceof Dialog && compName != null && compName.startsWith("dialog")) { //$NON-NLS-1$ compName = null; } else if (component instanceof JToolBar && compName != null && compName.startsWith("Tool Bar ")) { //$NON-NLS-1$ compName = null; } // -------------------------------------------------------- HierarchyContainer<Component> hierarchyParent = null; final Container parent = component.getParent(); if (parent != null) { hierarchyParent = getHierarchyContainer(parent); } if (hierarchyContainer.getName() != null && hierarchyContainer.getName().length() != 0) { return; } // isUniqueName is null safe, see description there int count = 1; String originalName = null; String newName = null; boolean newNameGenerated = (compName == null); if (compName != null) { originalName = compName; newName = compName; } if (newName == null) { while (!isUniqueName(hierarchyParent, newName, component)) { newName = createName(component, count); count++; } } else { while (!isUniqueName(hierarchyParent, newName, component)) { count++; newName = createName(originalName, count); } } hierarchyContainer.setName(newName, newNameGenerated); } } /** * Checks for uniqueness of <code>name</code> for the components in * <code>parent</code>.<br> * If parent is null every name is unique, a null name is NEVER unique. If * both parameters are null, false is returned. <br> * @param parent the hierarchy container containing the components which are checked. * @param name the name to check * @param swingComponent The component for which the name is being checked. * @return true if the name is treated as unique, false otherwise. */ private boolean isUniqueName(HierarchyContainer<Component> parent, String name, Component swingComponent) { if (name == null) { return false; } if (parent == null) { return true; } HierarchyContainer<Component>[] compIDs = parent.getComps(); final int length = compIDs.length; for (int index = 0; index < length; index++) { Component childComponent = compIDs[index].getCompID().getComponent(); Object childName = childComponent.getName(); if (name.equals(childName) && childComponent != swingComponent) { return false; } } for (int index = 0; index < length; index++) { if (name.equals(compIDs[index].getName())) { return false; } } return true; } /** * Returns all descendants of the given <code>component</code> * @param c a <code>component</code> value * @return a <code>collection</code> of the component's descendants or an * empty <code>collection</code> if nothing was found or <code>c</code> is null. */ private Collection<Component> getComponents(Component c) { if (c instanceof Container) { Container cont = (Container) c; List<Component> list = new ArrayList<Component>(); list.addAll(Arrays.asList(cont.getComponents())); if (c instanceof JMenu) { list.add(((JMenu) c).getPopupMenu()); } else if (c instanceof Window) { list.addAll(Arrays.asList(((Window) c).getOwnedWindows())); } else if (c instanceof JDesktopPane) { // add iconified frames, which are otherwise unreachable // for consistency, they are still considered children of // the desktop pane. int count = cont.getComponentCount(); for (int i = 0; i < count; i++) { Component child = cont.getComponent(i); if (child instanceof JInternalFrame.JDesktopIcon) { JInternalFrame frame = ((JInternalFrame.JDesktopIcon) child) .getInternalFrame(); if (frame != null) { list.add(frame); } } } } return list; } // an empty ArrayList return new ArrayList<Component>(); } /** * A window listener listening to window closed, registered to any opened window. <br> * @author BREDEX GmbH * @created 05.10.2004 * */ private class WindowClosingListener extends WindowAdapter { /** * {@inheritDoc} */ public void windowOpened(WindowEvent e) { windowActivated(e); } /** * {@inheritDoc} */ public void windowClosed(final WindowEvent event) { Runnable deregistrationRunnable = new Runnable() { public void run() { ClassLoader originalCL = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader( ((SwingAUTServer)AUTServer.getInstance()).getClass() .getClassLoader()); try { Window window = event.getWindow(); remove(window); if (log.isInfoEnabled()) { log.info("deregistering window listener from window " + window); //$NON-NLS-1$ } window.removeWindowListener(WindowClosingListener.this); } finally { Thread.currentThread() .setContextClassLoader(originalCL); } } }; registerListener(deregistrationRunnable); } /** * {@inheritDoc} */ public void windowDeactivated(WindowEvent e) { ClassLoader originalCL = Thread.currentThread() .getContextClassLoader(); Thread.currentThread().setContextClassLoader( ((SwingAUTServer)AUTServer.getInstance()).getClass() .getClassLoader()); try { Window window = e.getWindow(); if (!window.isVisible()) { remove(window); } } finally { Thread.currentThread().setContextClassLoader(originalCL); } } /** * {@inheritDoc} */ public void windowActivated(WindowEvent e) { ClassLoader originalCL = Thread.currentThread() .getContextClassLoader(); Thread.currentThread().setContextClassLoader( ((SwingAUTServer)AUTServer.getInstance()).getClass() .getClassLoader()); try { Window window = e.getWindow(); if (window.isVisible() && getHierarchyContainer(window) == null) { add(window); } } finally { Thread.currentThread().setContextClassLoader(originalCL); } } } /** * {@inheritDoc} */ public void componentHidden(ComponentEvent e) { checkDispatchThread(); Component component = e.getComponent(); if (component instanceof Window) { remove((Window) component); } else { removeComponent(component, component.getParent()); } } /** * {@inheritDoc} */ public void componentMoved(ComponentEvent e) { // Do nothing } /** * {@inheritDoc} */ public void componentResized(ComponentEvent e) { // Do nothing } /** * {@inheritDoc} */ public void componentShown(ComponentEvent e) { checkDispatchThread(); Component component = e.getComponent(); if (component instanceof Window) { add((Window) component); } else { addComponent(component); } } /** * Checks whether the current thread is the dispatch thread, and logs an * error if it is not. */ private void checkDispatchThread() { if (!EventQueue.isDispatchThread()) { // throw and catch an exception so we can get a stack trace try { throw new Exception(); } catch (Exception e) { log.error("Method called outside of the dispatch thread. This may indicate a potential error in the AUT.", e); //$NON-NLS-1$ } } } @Override public boolean isInActiveWindow(Component component) { if (component == null) { return false; } Window windowAncestor = SwingUtilities .getWindowAncestor(component); return (windowAncestor != null && windowAncestor.isActive()); } }