/*******************************************************************************
* 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.swt.components;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.apache.commons.lang.StringUtils;
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.swt.listener.ComponentHandler;
import org.eclipse.jubula.rc.swt.utils.SwtUtils;
import org.eclipse.jubula.tools.internal.constants.SwtToolkitConstants;
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.swt.SWTException;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
/**
* 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 SWT the ShellClosed event is not delivered properly, so a shell
* listener is added to any opened window listening to
* <code>ShellEvent.ShellCLosed</code>.<br><p>
* <b>Interferences with the AUT</b><ul>
* <li>The SwtAUTHierarchy is registered as a dispose listener to every
* widget from the AUT (but not to the hierarchy container).</li>
* <li>The SwtAUTHierarchy is registered as a window listener to every shell
* from the AUT.</li></ul>
* @author BREDEX GmbH
* @created 30.08.2004
*/
public class SwtAUTHierarchy extends AUTHierarchy<Widget> {
/** the logger */
private static AutServerLogger log = new AutServerLogger(
SwtAUTHierarchy.class);
/**
* Businessprocess for getting components
*/
private static FindSWTComponentBP findBP = new FindSWTComponentBP();
/** Mapping from Shells to their corresponding listeners */
private Map<Shell, ShellClosingListener> m_shellToListenerMap =
new HashMap<Shell, ShellClosingListener>();
/** The current active window */
private Shell m_activeWindow;
// methods operating on meta data, parameters are from the AUT
/**
* Adds the complete hierarchy of the given <code>window</code> to the
* hierarchy. <br>
* @param window a new (and opened) Shell
*/
public void add(Shell window) {
if (window == null || window.isDisposed()) {
return;
}
// 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
if (log.isInfoEnabled()) {
log.info("adding window " + window); //$NON-NLS-1$
}
// don't add, if in hierarchy map yet
if (getRealMap().get(window) != null) {
return;
}
if (getHierarchyContainer(window) != null) {
return;
}
// create a new HierarchyContainer for window
HierarchyContainer<Widget> hierarchyWindow = new SwtHierarchyContainer(
new SwtComponent(window));
// update the hash table
addToHierachyMap(hierarchyWindow);
// add a window listener for window closed events
registerAsWindowListener(window);
// get the parent of window, if any
Composite parent = window.getParent();
if (parent != null) {
HierarchyContainer<Widget> hierarchyParent =
getHierarchyContainer(parent);
if (hierarchyParent == null) {
// a new container, see comment at top of the method
hierarchyParent = new SwtHierarchyContainer(
new SwtComponent(parent));
name(hierarchyParent);
}
// add the new container for the window to hierarchyParent
hierarchyParent.add(hierarchyWindow);
name(hierarchyWindow);
// update m_hierarchyMap
addToHierachyMap(hierarchyParent);
addToHierarchyUp(hierarchyParent, parent);
}
// registering this class as a container listener happens in
// addToHierarchy
addToHierarchyDown(hierarchyWindow, window);
}
/**
* Removes the given window from the hierarchy.
*
* @param window the window to remove.
*/
private void remove(Shell window) {
// remove the shell closing listener, if it is still registered
// remove window from the map
// remove the container from hierarchyMap
if (log.isInfoEnabled()) {
log.info("deregistering window listener from window " + window); //$NON-NLS-1$
}
if (!window.isDisposed() && m_shellToListenerMap.containsKey(window)) {
window.removeShellListener(
m_shellToListenerMap.get(window));
}
m_shellToListenerMap.remove(window);
// Only remove the window if it is currently listed in the hierarchy
// (i.e. don't try to remove it twice)
if (getRealMap().get(window) != null) {
HierarchyContainer<Widget> windowContainer =
getHierarchyMap()
.get(getRealMap().get(window));
if (windowContainer != null) {
// remove the windowContainer from its parent in the hierarchy, if
// any
HierarchyContainer<Widget> 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 synchronized IComponentIdentifier getComponentIdentifier(
Widget component)
throws ComponentNotManagedException {
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<Widget> 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(Widget comp) {
List<String> context = new ArrayList<String>();
Widget widgetParent = SwtUtils.getWidgetParent(comp);
if (widgetParent != null) {
HierarchyContainer<Widget> parent =
getHierarchyContainer(widgetParent);
if (parent != null) {
HierarchyContainer<Widget>[] comps = parent.getComps();
for (int i = 0; i < comps.length; i++) {
Widget 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 synchronized IComponentIdentifier[] getAllComponentId() {
List<IComponentIdentifier> result = new Vector<IComponentIdentifier>();
Set<? extends AUTComponent<Widget>> keys = getHierarchyMap().keySet();
for (Iterator<? extends AUTComponent<Widget>> iter = keys.iterator();
iter.hasNext();) {
Widget 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.getClass().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 Widget findComponent(IComponentIdentifier componentIdentifier)
throws IllegalArgumentException, ComponentNotManagedException,
InvalidDataException {
final Widget comp = (Widget)findBP.findComponent(componentIdentifier,
ComponentHandler.getAutHierarchy());
if (comp != null) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
Shell shell = SwtUtils.getShell(comp);
try {
if (shell != null && shell.isVisible()) {
Shell activeShell = shell.getDisplay()
.getActiveShell();
if (activeShell != shell
&& !SwtUtils.isDropdownListShell(
activeShell)) {
shell.setActive();
}
}
} catch (SWTException swte) {
// do nothing if a shell is disposed while accessing
}
}
});
return comp;
}
throw new ComponentNotManagedException(
"unmanaged component with identifier: '" //$NON-NLS-1$
+ componentIdentifier.toString() + "'.", //$NON-NLS-1$
MessageIDs.E_COMPONENT_NOT_MANAGED);
}
/**
* 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.
*/
private List<String> getPathToRoot(Widget 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<Widget> autContainer =
getHierarchyContainer(component);
if (autContainer != null) {
// add the name of the container itself
hierarchy.add(autContainer.getName());
HierarchyContainer<Widget> parent = autContainer.getPrnt();
// prepend the name of the container up to the root container
while (parent != null) {
((ArrayList<String>)hierarchy).add(0, parent.getName());
parent = parent.getPrnt();
}
} else {
log.error("component '" + component //$NON-NLS-1$
+ "' is not managed by this hierarchy"); //$NON-NLS-1$
throw new ComponentNotManagedException(
"unmanaged component " //$NON-NLS-1$
+ component.toString(),
MessageIDs.E_COMPONENT_NOT_MANAGED);
}
return hierarchy;
}
/**
* Refresh all children components for the given shell
* @param shell The shell to refresh.
*
*/
public void refreshShell(Shell shell) {
if (m_shellToListenerMap.containsKey(shell)
|| getRealMap().containsKey(shell)) {
remove(shell);
}
add(shell);
}
/**
* Adds the given component to the hierarchy if it does
* not already exist there. Overwrites the current entry in the hierarchy if
* there is already an entry and that component is <em>not</em> equal to
* the given component. Otherwise does nothing.
* @param toRefresh The component to refresh
*/
public synchronized void refreshComponent(Widget toRefresh) {
HierarchyContainer<Widget> currentHierarchyContainer =
getHierarchyContainer(toRefresh);
if (currentHierarchyContainer == null) {
componentAdded(toRefresh);
} else if (toRefresh
!= currentHierarchyContainer.getCompID().getComponent()) {
componentRemoved(toRefresh);
componentAdded(toRefresh);
} else {
// hierarchy container exists and represents same component, so
// refresh the children
HierarchyContainer<Widget> [] childContainers =
currentHierarchyContainer.getComps();
for (int i = 0; i < childContainers.length; i++) {
removeFromHierarchy(childContainers[i]);
}
addToHierarchyDown(currentHierarchyContainer, toRefresh);
}
}
/**
*
* @param toRefresh The component for which the name should be refreshed.
*/
public synchronized void refreshComponentName(Widget toRefresh) {
rename(getHierarchyContainer(toRefresh));
}
/**
* Check if a the Widget is a Control and is not visible.
* An SWTException is not thrown, if the widget is disposed.
* @param widget The widget to check.
* @return True, if the Widget is a Control and it is not visible, otherwise false.
*/
private boolean isInstanceofControlAndNotVisible(Widget widget) {
if (widget instanceof Control) {
try {
Control c = (Control) widget;
return !c.isVisible();
} catch (SWTException swte) {
// do nothing
}
}
return false;
}
/**
* Check if a shell is visible. An SWTException is not thrown, if the shell is disposed.
* @param shell The shell to check.
* @return True, if the shell is currently visible, otherwise false.
*/
private boolean isShellVisible(Shell shell) {
try {
return shell.isVisible();
} catch (SWTException swte) {
// do nothing
}
return false;
}
/**
* Add the new component to the hierarchy if it is not already there.
* @param toAdd The component to add to the hierarchy.
*/
public synchronized void componentAdded(Widget toAdd) {
if (toAdd == null || toAdd.isDisposed()) {
// Do not add null or disposed components
return;
}
if (isInstanceofControlAndNotVisible(toAdd)) {
// Do not add invisible components
return;
}
if (toAdd instanceof Shell) {
add((Shell)toAdd);
}
if (getHierarchyContainer(toAdd) != null) {
return;
}
ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
try {
Widget container = SwtUtils.getWidgetParent(toAdd);
pruneHierarchy(container);
if (container == null || container.isDisposed()) {
// Parent for added component does not exist or has been
// disposed, so do not add the component.
return;
}
if (log.isDebugEnabled()) {
log.debug("component '" + toAdd //$NON-NLS-1$
+ "' added to '" + container //$NON-NLS-1$
+ "'"); //$NON-NLS-1$
}
// get the hierarchy container for container, must be there!
HierarchyContainer<Widget> hierarchyContainer = null;
if (toAdd instanceof Shell) {
hierarchyContainer = getHierarchyContainer(toAdd);
} else {
hierarchyContainer = getHierarchyContainer(container);
}
if (hierarchyContainer == null) {
// This can happen if a new composite is created after a window
// is activated. The widgets within the composite fire paint
// events, but the composite itself never does.
log.info("component added to unmanaged container '" //$NON-NLS-1$
+ container + "'; adding the container."); //$NON-NLS-1$
componentAdded(container);
hierarchyContainer = getHierarchyContainer(container);
if (hierarchyContainer == null) {
log.info("addition of container '" //$NON-NLS-1$
+ container + "' failed. This may be because the " //$NON-NLS-1$
+ "container is not visible."); //$NON-NLS-1$
}
}
if (hierarchyContainer != null
&& getHierarchyContainer(toAdd) == null) {
// create new hierarchy container for child, name, update hashtable, put
// them together,
HierarchyContainer<Widget> hierarchyChild =
new SwtHierarchyContainer(new SwtComponent(toAdd));
addToHierachyMap(hierarchyChild);
hierarchyContainer.add(hierarchyChild);
toAdd.addDisposeListener(new ComponentDisposingListener());
name(hierarchyChild);
addToHierarchyDown(hierarchyChild, toAdd);
}
} finally {
Thread.currentThread().setContextClassLoader(originalCL);
}
}
/**
* Follows the from the given container to the root element in the
* component hierarchy, removing non-visible child elements along the
* way.
*
* @param container The component to start at.
*/
private void pruneHierarchy(Widget container) {
Widget currentWidget = container;
while (currentWidget != null) {
pruneChildren(currentWidget);
currentWidget = SwtUtils.getWidgetParent(currentWidget);
}
}
/**
* Removes all non-visible children of the given container from the
* component hierarchy.
*
* @param container The container to look through.
*/
private void pruneChildren(Widget container) {
HierarchyContainer<Widget> hierarchyContainer =
getHierarchyContainer(container);
if (hierarchyContainer != null) {
HierarchyContainer<Widget>[] childContainers =
hierarchyContainer.getComps();
for (int i = 0; i < childContainers.length; i++) {
Widget child = childContainers[i].getCompID().getComponent();
if (child.isDisposed()
|| isInstanceofControlAndNotVisible(child)) {
componentRemoved(child);
}
}
}
}
/**
* Remove the given component from the hierarchy.
* @param toRemove The component to remove
*/
public synchronized void componentRemoved(Widget toRemove) {
if (toRemove instanceof Shell) {
remove((Shell)toRemove);
return;
}
final ClassLoader originalCL = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
try {
if (log.isDebugEnabled()) {
log.debug("removing component '" + toRemove + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
// remove the child from hash table
final Object componentToRemove = getRealMap().get(toRemove);
if (componentToRemove != null) {
HierarchyContainer<Widget> hierarchyContainer =
getHierarchyMap().remove(componentToRemove);
// update the hierarchy and deletes the container and component from maps
removeFromHierarchy(hierarchyContainer);
} else {
// child was not in the hierarchy map
// -> log this
log.debug("an unmanaged component was removed: " + toRemove); //$NON-NLS-1$
}
} finally {
Thread.currentThread().setContextClassLoader(originalCL);
}
}
/**
* register a window listener to <code>window</code>.<br> deregistering happens in
* <code>ShellClosingListener.shellClosed()</code>.
* @param window the window to register to
*/
private void registerAsWindowListener(Shell window) {
if (log.isInfoEnabled()) {
log.info("registering window listener to shell " //$NON-NLS-1$
+ window);
}
if (isListening(window)) {
return;
}
ShellClosingListener listener = new ShellClosingListener();
m_shellToListenerMap.put(window, listener);
window.addShellListener(listener);
window.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
remove((Shell)e.widget);
}
});
}
/**
*
* @param window The window to check
* @return <code>true</code> if the window currently has an associated
* <code>ShellClosingListener</code>. Otherwise <code>false</code>.
*/
private boolean isListening(Shell window) {
return m_shellToListenerMap.containsKey(window);
}
/**
* 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 hierarchyContainer of container
* @param container the container from the AUT
*/
private synchronized void addToHierarchyUp(
HierarchyContainer<Widget> hierarchyContainer,
Composite container) {
if (log.isInfoEnabled()) {
log.info("addToHierarchyUp: " //$NON-NLS-1$
+ hierarchyContainer
+ "," + container); //$NON-NLS-1$
}
Composite parent = container.getParent();
if (parent != null) { // root not reached
HierarchyContainer<Widget> hierarchyParent =
getHierarchyContainer(container);
if (hierarchyParent == null) {
// unknown hierarchyContainer for parent:
// create new hierarchy container, name it,
// add current hierarchyContainer to parent hierarchy,
// update map m_hierarchyMap
// register listener
// recursion
hierarchyParent = new SwtHierarchyContainer(
new SwtComponent(parent));
hierarchyParent.add(hierarchyContainer);
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 synchronized void addToHierarchyDown(
HierarchyContainer<Widget> hierarchyContainer,
Widget container) {
Widget hierarchyComponent =
hierarchyContainer.getCompID().getComponent();
if (container == null || container.isDisposed()
|| hierarchyComponent == null || hierarchyComponent.isDisposed()) {
return;
}
if (log.isInfoEnabled()) {
log.info("addToHierarchyDown: " //$NON-NLS-1$
+ hierarchyContainer
+ "," + container); //$NON-NLS-1$
}
name(hierarchyContainer);
Collection<Widget> collection = getComponents(container);
for (Iterator<Widget> iter = collection.iterator(); iter.hasNext();) {
Widget comp = iter.next();
if (comp == null) {
continue;
}
if (getHierarchyContainer(comp) != null) {
return;
}
// add the container
componentAdded(comp);
}
}
/**
* 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<Widget> container) {
if (container == null) {
return;
}
HierarchyContainer<Widget> parentContainer = container.getPrnt();
if (parentContainer != null) {
parentContainer.remove(container);
}
AUTComponent<Widget> autCompID = container.getCompID();
Widget autComp = autCompID.getComponent();
if (autComp == null) {
log.error("invalid component for removal:" //$NON-NLS-1$
+ autCompID.toString());
}
removeFromHierachyMap(container);
if (!autComp.isDisposed()) {
Collection<Widget> childs = getComponents(autComp);
Widget widget;
for (Iterator<Widget> iter = childs.iterator(); iter.hasNext();) {
widget = iter.next();
if (widget != null) {
removeFromHierarchy(getHierarchyContainer(widget));
}
}
}
}
/**
* 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 hierachy container or null if the component is not yet managed
*/
public HierarchyContainer<Widget> getHierarchyContainer(Widget component)
throws IllegalArgumentException {
Validate.notNull(component, "The component must not be null"); //$NON-NLS-1$
HierarchyContainer<Widget> result = null;
try {
SwtComponent compID = (SwtComponent)getRealMap().get(component);
if (compID != null) {
result = getHierarchyMap().get(compID);
}
} catch (ClassCastException cce) {
log.error(cce);
} catch (NullPointerException npe) {
log.error(npe);
}
return result;
}
/**
* @param widget the component to check, whether it's disappeared or not
* @return true, if the component disappeared
*/
public boolean isComponentInHierarchy(Widget widget) {
if (widget == null) {
return false;
}
return getHierarchyContainer(widget) != null;
}
/**
* 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 hierarchyContainer to name, if hierarchyContainer is null,
* no action is performed and no exception is thrown.
*/
private synchronized void name(
HierarchyContainer<Widget> hierarchyContainer) {
if (hierarchyContainer != null) {
Widget component =
hierarchyContainer.getCompID().getComponent();
HierarchyContainer<Widget> hierarchyParent = null;
Widget parent = SwtUtils.getWidgetParent(component);
if (parent != null) {
hierarchyParent = getHierarchyContainer(parent);
// for some reason, this check is necessary in order to prevent
// orphaning the hierarchy container.
// https://bxapps.bredex.de/bugzilla/show_bug.cgi?id=216
// occurred when this null-check was not present.
if (hierarchyParent != null) {
hierarchyContainer.setPrnt(hierarchyParent);
}
}
if (StringUtils.isEmpty(hierarchyContainer.getName())) {
rename(hierarchyContainer);
}
}
}
/**
* Renames the given hierarchy container (or just names it, if it did not
* previously have a name).
*
* @param hierarchyContainer The container to rename.
*/
private synchronized void rename(
HierarchyContainer<Widget> hierarchyContainer) {
if (hierarchyContainer != null) {
Widget component =
hierarchyContainer.getCompID().getComponent();
String compName = FindSWTComponentBP.getComponentName(component);
HierarchyContainer<Widget> hierarchyParent =
hierarchyContainer.getPrnt();
// isUniqueName is null safe, see description there
int count = 1;
String originalName = null;
String newName = null;
Object rcpCompId = component
.getData(SwtToolkitConstants.RCP_NAME);
boolean newNameGenerated = false;
if (compName != null) {
newName = compName.toString();
originalName = compName.toString();
} else if (rcpCompId != null) {
newName = rcpCompId.toString();
originalName = rcpCompId.toString();
}
if (newName == null) {
newNameGenerated = true;
while (!isUniqueName(hierarchyParent, component, newName)) {
newName = createName(component, count);
count++;
}
} else {
while (!isUniqueName(hierarchyParent, component, newName)) {
newName = createName(originalName, count);
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 widget the widget that might receive the checked name
* @param name the name to check
* @return true if the name is treated as unique, false otherwise.
*/
private boolean isUniqueName(HierarchyContainer<Widget> parent,
Widget widget, String name) {
if (name == null) {
return false;
}
if (parent == null) {
return true;
}
HierarchyContainer<Widget>[] compIDs = parent.getComps();
final int length = compIDs.length;
for (int index = 0; index < length; index++) {
Widget childWidget =
compIDs[index].getCompID().getComponent();
if (childWidget != null && !childWidget.isDisposed()) {
String childWidgetName = FindSWTComponentBP
.getComponentName(childWidget);
if ((childWidget != widget) && name.equals(childWidgetName)) {
return false;
}
}
}
for (int index = 0; index < length; index++) {
if (name.equals(compIDs[index].getName())) {
return false;
}
}
return true;
}
// methods operating on meta data or on the instances of the AUT, depending
// on the given parameter
/**
* Returns all descendants of the given <code>component</code>
* @param component a <code>Widget</code>
* @return a collection of all components in the hierarchy empty <code>collection</code> if nothing was found or <code>c</code> is null.
*/
private Collection<Widget> getComponents(Widget component) {
List<Widget> children = new LinkedList<Widget>();
if (component instanceof Composite) {
Composite cont = (Composite) component;
children.addAll(Arrays.asList(cont.getChildren()));
}
children.addAll(Arrays.asList(SwtUtils.getMappableItems(component)));
return children;
}
/**
* Registered to all components that are added to the hierarchy.
* Removes the component from the hierarchy (along with all child
* components) if the corresponding widget is disposed.
*
* @author BREDEX GmbH
* @created Jul 13, 2007
*/
private class ComponentDisposingListener implements DisposeListener {
/**
* {@inheritDoc}
*/
public void widgetDisposed(DisposeEvent event) {
// BE CAREFUL, the widget (i.e. event.widget) is disposed at this
// time, so a lot of methods are not allowed to be called,
// removeDisposeListener() among them
componentRemoved(event.widget);
}
}
/**
* A shell listener listening to Shell closed, registered to any opened
* shell. <br>
*
* @author BREDEX GmbH
* @created 05.10.2004
*/
private class ShellClosingListener extends ShellAdapter {
/**
* {@inheritDoc}
*/
public void shellClosed(ShellEvent event) {
ClassLoader originalCL = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(
AUTServer.getInstance().getClass().getClassLoader());
try {
Shell window = (Shell)event.widget;
remove(window);
} finally {
Thread.currentThread().setContextClassLoader(originalCL);
}
}
/**
* {@inheritDoc}
*/
public void shellDeactivated(ShellEvent e) {
ClassLoader originalCL = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(
AUTServer.getInstance().getClass().getClassLoader());
try {
Shell window = (Shell)e.widget;
if (!isShellVisible(window)) {
remove(window);
}
} finally {
Thread.currentThread().setContextClassLoader(originalCL);
}
}
/**
* {@inheritDoc}
*/
public void shellActivated(ShellEvent e) {
ClassLoader originalCL = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(
AUTServer.getInstance().getClass().getClassLoader());
try {
Shell window = (Shell)e.widget;
m_activeWindow = window;
if (isShellVisible(window)
&& getHierarchyContainer(window) == null) {
add(window);
}
} finally {
Thread.currentThread().setContextClassLoader(originalCL);
}
}
}
@Override
public boolean isInActiveWindow(Widget component) {
if (component == null) {
return false;
}
boolean searchTopParentComponent = true;
Widget componentToCheck = component;
while (searchTopParentComponent) {
Widget parent = SwtUtils.getWidgetParent(componentToCheck);
if (parent == null) {
searchTopParentComponent = false;
} else {
componentToCheck = parent;
}
}
if (componentToCheck != null && m_activeWindow != null
&& componentToCheck.equals(m_activeWindow)) {
return true;
}
return false;
}
}