// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.jfcmonitor; import java.awt.Component; import java.awt.Container; import java.awt.event.ContainerListener; import java.beans.PropertyChangeListener; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.accessibility.AccessibleContext; import de.ugoe.cs.util.StringTools; /** *

* This class manages information about the current GUI. It always contains the current GUI * hierarchy. *

* * @author Steffen Herbold * @author Fabian Glaser * @version 1.0 */ public class JFCComponent { /** *

* Map of all known GUI components. *

*/ private static Map knownComponents = new HashMap(); /** *

* List of PropertyChangeListeners that are registered on the components. *

*/ private static List propertyChangeListeners = new ArrayList(); /** *

* List of ContainerListeners that are registered on the components. *

*/ private static List containerListeners = new ArrayList(); /** *

* Adds a AWT component to the GUI hierarchy. If the component already exists in the hierarchy, * it is not added a second time. *

* * @param component * component that is added */ public static void add(Component component) { add(component, find(component.getParent())); } /** *

* Adds a new PropertyChangeListener to the components. *

* @param list * the PropertyChangeListener */ public static void addPropertyChangeListener(PropertyChangeListener list){ propertyChangeListeners.add(list); } /** *

* Adds a new ContainerListener to the components. *

* @param list * the ContainerListener */ public static void addContainerListener(ContainerListener list){ containerListeners.add(list); } /** *

* Tests if a component is already known. *

* @param component to be tested * @return true, if component is already known, false otherwise. */ public static boolean isKnown(Component component){ if (knownComponents.containsKey(component)) return true; return false; } /** *

* Adds a AWT component to the GUI hierarchy. If the component already exists in the hierarchy, * it is not added a second time. *

* * @param component * component that is added * @param parent * parent of the component */ public static void add(Component component, JFCComponent parent) { if (!knownComponents.containsKey(component)) { knownComponents.put(component, new JFCComponent(component, parent)); } } /** *

* Finds a component in the GUI hierarchy and returns the corresponding JFComponent instance. * Returns null if the component is not found. *

* * @param component * component that is searched for * @return corresponding JFComponent instance; null if the component is not found */ public static JFCComponent find(Component component) { return knownComponents.get(component); } /** *

* Removes a component from the GUI hierarchy. In case the component is not part of the known * hierachy, nothing happens. *

* * @param component * component to be removed */ public static void remove(Component component) { JFCComponent jfcComponent = knownComponents.remove(component); if (jfcComponent != null) { jfcComponent.removeFromParent(); jfcComponent.removeChildren(); } } /** *

* Parent of the GUI component. null means, that the component has no parent. *

*/ private JFCComponent parent = null; /** *

* Child components of the component. *

*/ private List children = new LinkedList(); /** *

* Reference to the actual GUI component. *

*/ private Component component; /** *

* Helper attribute that contains the title of the component. Set by {@link #setTitle()}. *

*/ private String title = null; /** *

* Helper attribute that encodes the source of the title. *

*/ private int titleSource = JFCComponentTitleHierachy.SOURCE_NOT_KNOWN; /** *

* Helper attribute that contains the class of the component. Set by {@link #setClass()}. *

*/ private String componentClass = null; /** *

* Helper attribute that contains the icon of the component. Set by {@link #setIcon()}. *

*/ private String icon = null; /** *

* Helper attribute that contains the icon of the component. Set by {@link #setIndex()}. *

*/ private int index = -1; /** *

* Constructor. Creates a new JFCComponent. Only used internally by * {@link #add(Component, JFCComponent)}. *

* * @param component * component associated with the JFCComponent * @param parent * parent of the component; null if there is no parent */ private JFCComponent(Component component, JFCComponent parent) { if (component == null) { throw new InvalidParameterException("parameter component must not be null"); } this.component = component; // add PropertyChangeListeners to AccessibleContext AccessibleContext context = component.getAccessibleContext(); if (context != null){ for (PropertyChangeListener listener: propertyChangeListeners) context.addPropertyChangeListener(listener); } // add PropertyChangeListeners to component itself for (PropertyChangeListener listener: propertyChangeListeners) this.component.addPropertyChangeListener(listener); this.parent = parent; if (parent != null) { parent.addChild(this); } if (component instanceof Container) { for (ContainerListener listener: containerListeners) ((Container) component).addContainerListener(listener); for (Component childComponent : ((Container) component).getComponents()) { add(childComponent, this); } } } /** *

* Adds a child component to the current component. *

* * @param child * child component to be added */ private void addChild(JFCComponent child) { children.add(child); } /** *

* Returns an XML representation of the component. *

* * @return XLM snippet */ public String getXML() { setClass(); setIcon(); setIndex(); setTitle(); StringBuilder builder = new StringBuilder(); builder.append("" + StringTools.ENDLINE); // title might contain html-Tags (e.g. in ArgoUML) --> must be escaped builder.append(" " + StringTools.ENDLINE); builder.append(" " + StringTools.ENDLINE); builder.append(" " + StringTools.ENDLINE); builder.append(" " + StringTools.ENDLINE); if (parent != null){ if (!JFCComponent.isKnown(parent.component)) throw new AssertionError("Referenced parent is not known."); builder.append(" " + StringTools.ENDLINE); } builder.append(getClassHierarchy()); builder.append("" + StringTools.ENDLINE); return builder.toString(); } /** *

* Returns an integer that encodes the source of the title. *

* @return int */ public int getTitleSource(){ return titleSource; } /** *

* Returns an XML representation of the components children. *

* @return XML representation of children */ public String printChildren(){ StringBuilder builder = new StringBuilder(); for (JFCComponent child: children){ builder.append(child.getXML()); builder.append(child.printChildren()); } return builder.toString(); } /** *

* Removes a child component from the current component. *

* * @param child * child component to be removed */ private void removeChild(JFCComponent child) { children.remove(child); } /** *

* Removes the component from the list of children of its parent. *

*/ private void removeFromParent() { if (parent != null) { parent.removeChild(this); } } /** *

* Triggers the removals of all child components from the GUI hierarchy, i.e., calls * {@link #remove(Component)} for all child components. *

*/ private void removeChildren() { for (JFCComponent child : children) { remove(child.component); } } /** *

* Sets the {@link #title} of the component. The title is defined as follows (first in the list, * that is not null): *

    *
  • accessible name of the component if available
  • *
  • {@link #icon} of the component
  • *
  • name of the component
  • *
  • coordinates of the component
  • *
*

*/ public void setTitle() { // Note that JFCComponentTitleHierarchy depends on this method. So any changes made // here should be reflected in JFCComponentTitleHierarchy. title = null; // reset title AccessibleContext accessibleContext = component.getAccessibleContext(); if (accessibleContext != null) { title = accessibleContext.getAccessibleName(); titleSource = JFCComponentTitleHierachy.ACCESSIBLE_NAME; } if (title == null) { title = icon; titleSource = JFCComponentTitleHierachy.ICON; } if (title == null) { title = component.getName(); titleSource = JFCComponentTitleHierachy.NAME; } if (title == null) { // use coordinates as last resort title = "Pos(" + component.getX() + "," + component.getY() + ")"; titleSource = JFCComponentTitleHierachy.POS; } } /** *

* Sets the {@link #componentClass} of the component. *

*/ private void setClass() { componentClass = component.getClass().getName(); } /** *

* Sets the {@link #icon} of the component. *

*/ private void setIcon() { icon = null; // reset icon Method getIconMethod; try { getIconMethod = component.getClass().getMethod("getIcon", new Class[0]); if (getIconMethod != null) { Object iconObject = getIconMethod.invoke(component, new Object[] { }); if (iconObject != null) { String iconPath = iconObject.toString(); if (!iconPath.contains("@")) { System.out.println("iconPath"); String[] splitResult = iconPath.split(File.separatorChar == '\\' ? "\\\\" : File.separator); icon = splitResult[splitResult.length - 1]; } } } } catch (SecurityException e) {} catch (NoSuchMethodException e) {} catch (IllegalArgumentException e) {} catch (IllegalAccessException e) {} catch (InvocationTargetException e) { System.err.println("Found method with name " + "getIcon" + " but could not access it."); } } /** *

* Sets the {@link #index} of the component as the index in the parent, if it is accessible. *

*/ private void setIndex() { index = -1; // reset index AccessibleContext accessibleContext = component.getAccessibleContext(); if (accessibleContext != null) { index = accessibleContext.getAccessibleIndexInParent(); } } /** *

* Constructs a string that represents inheritance of component. *

* @return */ private String getClassHierarchy(){ StringBuilder builder = new StringBuilder(); Class classobject = component.getClass(); builder.append(" " + StringTools.ENDLINE); while(classobject != null){ builder.append(" " + StringTools.ENDLINE); classobject = classobject.getSuperclass(); } builder.append(" " + StringTools.ENDLINE); return builder.toString(); } }