source: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCComponent.java

Last change on this file was 1048, checked in by pharms, 11 years ago
  • replaced call to apache commons to call usually used in AutoQUEST
  • Property svn:mime-type set to text/plain
File size: 14.8 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.jfcmonitor;
16
17import java.awt.Component;
18import java.awt.Container;
19import java.awt.event.ContainerListener;
20import java.beans.PropertyChangeListener;
21import java.io.File;
22import java.lang.reflect.InvocationTargetException;
23import java.lang.reflect.Method;
24import java.security.InvalidParameterException;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.LinkedList;
28import java.util.List;
29import java.util.Map;
30
31import javax.accessibility.AccessibleContext;
32
33import de.ugoe.cs.util.StringTools;
34
35/**
36 * <p>
37 * This class manages information about the current GUI. It always contains the current GUI
38 * hierarchy.
39 * </p>
40 *
41 * @author Steffen Herbold
42 * @author Fabian Glaser
43 * @version 1.0
44 */
45public class JFCComponent {
46
47    /**
48     * <p>
49     * Map of all known GUI components.
50     * </p>
51     */
52    private static Map<Component, JFCComponent> knownComponents =
53        new HashMap<Component, JFCComponent>();
54   
55    /**
56     * <p>
57     * List of PropertyChangeListeners that are registered on the components.
58     * </p>
59     */
60    private static List<PropertyChangeListener> propertyChangeListeners =
61        new ArrayList<PropertyChangeListener>();
62   
63    /**
64     * <p>
65     * List of ContainerListeners that are registered on the components.
66     * </p>
67     */
68    private static List<ContainerListener> containerListeners =
69                new ArrayList<ContainerListener>();
70
71    /**
72     * <p>
73     * Adds a AWT component to the GUI hierarchy. If the component already exists in the hierarchy,
74     * it is not added a second time.
75     * </p>
76     *
77     * @param component
78     *            component that is added
79     */
80    public static void add(Component component) {
81        add(component, find(component.getParent()));
82    }
83   
84    /**
85     * <p>
86     * Adds a new PropertyChangeListener to the components.
87     * </p>
88     * @param list
89     *                  the PropertyChangeListener
90     */
91    public static void addPropertyChangeListener(PropertyChangeListener list){
92        propertyChangeListeners.add(list);
93    }
94   
95    /**
96     * <p>
97     * Adds a new ContainerListener to the components.
98     * </p>
99     * @param list
100     *                  the ContainerListener
101     */
102    public static void addContainerListener(ContainerListener list){
103        containerListeners.add(list);
104    }
105   
106    /**
107     * <p>
108     * Tests if a component is already known.
109     * </p>
110     * @param component to be tested
111     * @return true, if component is already known, false otherwise.
112     */
113    public static boolean isKnown(Component component){
114        if (knownComponents.containsKey(component))
115                return true;
116        return false;
117    }
118
119    /**
120     * <p>
121     * Adds a AWT component to the GUI hierarchy. If the component already exists in the hierarchy,
122     * it is not added a second time.
123     * </p>
124     *
125     * @param component
126     *            component that is added
127     * @param parent
128     *            parent of the component
129     */
130    public static void add(Component component, JFCComponent parent) {
131        if (!knownComponents.containsKey(component)) {
132            knownComponents.put(component, new JFCComponent(component, parent));
133        }
134    }
135
136    /**
137     * <p>
138     * Finds a component in the GUI hierarchy and returns the corresponding JFComponent instance.
139     * Returns null if the component is not found.
140     * </p>
141     *
142     * @param component
143     *            component that is searched for
144     * @return corresponding JFComponent instance; null if the component is not found
145     */
146    public static JFCComponent find(Component component) {
147        return knownComponents.get(component);
148    }
149
150    /**
151     * <p>
152     * Removes a component from the GUI hierarchy. In case the component is not part of the known
153     * hierachy, nothing happens.
154     * </p>
155     *
156     * @param component
157     *            component to be removed
158     */
159    public static void remove(Component component) {
160        JFCComponent jfcComponent = knownComponents.remove(component);
161        if (jfcComponent != null) {
162            jfcComponent.removeFromParent();
163            jfcComponent.removeChildren();
164        }
165    }
166
167    /**
168     * <p>
169     * Parent of the GUI component. null means, that the component has no parent.
170     * </p>
171     */
172    private JFCComponent parent = null;
173
174    /**
175     * <p>
176     * Child components of the component.
177     * </p>
178     */
179    private List<JFCComponent> children = new LinkedList<JFCComponent>();
180
181    /**
182     * <p>
183     * Reference to the actual GUI component.
184     * </p>
185     */
186    private Component component;
187
188    /**
189     * <p>
190     * Helper attribute that contains the title of the component. Set by {@link #setTitle()}.
191     * </p>
192     */
193    private String title = null;
194   
195    /**
196     * <p>
197     * Helper attribute that encodes the source of the title.
198     * </p>
199     */
200    private int titleSource = JFCComponentTitleHierachy.SOURCE_NOT_KNOWN;
201
202    /**
203     * <p>
204     * Helper attribute that contains the class of the component. Set by {@link #setClass()}.
205     * </p>
206     */
207    private String componentClass = null;
208
209    /**
210     * <p>
211     * Helper attribute that contains the icon of the component. Set by {@link #setIcon()}.
212     * </p>
213     */
214    private String icon = null;
215
216    /**
217     * <p>
218     * Helper attribute that contains the icon of the component. Set by {@link #setIndex()}.
219     * </p>
220     */
221    private int index = -1;
222
223    /**
224     * <p>
225     * Constructor. Creates a new JFCComponent. Only used internally by
226     * {@link #add(Component, JFCComponent)}.
227     * </p>
228     *
229     * @param component
230     *            component associated with the JFCComponent
231     * @param parent
232     *            parent of the component; null if there is no parent
233     */
234    private JFCComponent(Component component, JFCComponent parent) {
235        if (component == null) {
236            throw new InvalidParameterException("parameter component must not be null");
237        }
238        this.component = component;
239       
240        // add PropertyChangeListeners to AccessibleContext
241        AccessibleContext context = component.getAccessibleContext();
242        if (context != null){
243                for (PropertyChangeListener listener: propertyChangeListeners)
244                        context.addPropertyChangeListener(listener);
245        }
246       
247        // add PropertyChangeListeners to component itself
248        for (PropertyChangeListener listener: propertyChangeListeners)
249                this.component.addPropertyChangeListener(listener);
250       
251        this.parent = parent;
252        if (parent != null) {
253            parent.addChild(this);
254        }
255
256        if (component instanceof Container) {
257                for (ContainerListener listener: containerListeners)
258                        ((Container) component).addContainerListener(listener);
259               
260            for (Component childComponent : ((Container) component).getComponents()) {
261                add(childComponent, this);
262            }
263        }
264    }
265
266    /**
267     * <p>
268     * Adds a child component to the current component.
269     * </p>
270     *
271     * @param child
272     *            child component to be added
273     */
274    private void addChild(JFCComponent child) {
275        children.add(child);
276    }
277
278    /**
279     * <p>
280     * Returns an XML representation of the component.
281     * </p>
282     *
283     * @return XLM snippet
284     */
285    public String getXML() {
286        setClass();
287        setIcon();
288        setIndex();
289        setTitle();
290        StringBuilder builder = new StringBuilder();
291        builder.append("<component hash=\"");
292        builder.append(Integer.toHexString(component.hashCode()) + "\" >" + StringTools.ENDLINE);
293        // title might contain html-Tags (e.g. in ArgoUML) --> must be escaped
294        builder.append(" <param name=\"title\" value=\"" + StringTools.xmlEntityReplacement(title) + "\" />" + StringTools.ENDLINE);
295        builder.append(" <param name=\"class\" value=\"" + componentClass + "\" />" +
296            StringTools.ENDLINE);
297        builder.append(" <param name=\"icon\" value=\"" + icon + "\" />" + StringTools.ENDLINE);
298        builder.append(" <param name=\"index\" value=\"" + index + "\" />" + StringTools.ENDLINE);
299        if (parent != null){
300                if (!JFCComponent.isKnown(parent.component))
301                        throw new AssertionError("Referenced parent is not known.");
302                builder.append(" <param name=\"parent\" value=\"" + Integer.toHexString(parent.component.hashCode()));
303                builder.append("\" />" + StringTools.ENDLINE);
304        }
305        builder.append(getClassHierarchy());
306        builder.append("</component>" + StringTools.ENDLINE);
307       
308        return builder.toString();
309    }
310   
311    /**
312     * <p>
313     * Returns an integer that encodes the source of the title.
314     * </p>
315     * @return int
316     */
317    public int getTitleSource(){
318        return titleSource;
319    }
320   
321    /**
322     * <p>
323     * Returns an XML representation of the components children.
324     * </p>
325     * @return XML representation of children
326     */
327    public String printChildren(){
328        StringBuilder builder = new StringBuilder();
329        for (JFCComponent child: children){
330                builder.append(child.getXML());
331                builder.append(child.printChildren());
332        }
333        return builder.toString();
334    }
335
336    /**
337     * <p>
338     * Removes a child component from the current component.
339     * </p>
340     *
341     * @param child
342     *            child component to be removed
343     */
344    private void removeChild(JFCComponent child) {
345        children.remove(child);
346    }
347
348    /**
349     * <p>
350     * Removes the component from the list of children of its parent.
351     * </p>
352     */
353    private void removeFromParent() {
354        if (parent != null) {
355            parent.removeChild(this);
356        }
357    }
358
359    /**
360     * <p>
361     * Triggers the removals of all child components from the GUI hierarchy, i.e., calls
362     * {@link #remove(Component)} for all child components.
363     * </p>
364     */
365    private void removeChildren() {
366        for (JFCComponent child : children) {
367            remove(child.component);
368        }
369    }
370
371    /**
372     * <p>
373     * Sets the {@link #title} of the component. The title is defined as follows (first in the list,
374     * that is not null):
375     * <ul>
376     * <li>accessible name of the component if available</li>
377     * <li>{@link #icon} of the component</li>
378     * <li>name of the component</li>
379     * <li>coordinates of the component</li>
380     * </ul>
381     * </p>
382     */
383    public void setTitle() {
384        // Note that JFCComponentTitleHierarchy depends on this method. So any changes made
385        // here should be reflected in JFCComponentTitleHierarchy.
386       
387        title = null; // reset title
388
389        AccessibleContext accessibleContext = component.getAccessibleContext();
390        if (accessibleContext != null) {
391            title = accessibleContext.getAccessibleName();
392            titleSource = JFCComponentTitleHierachy.ACCESSIBLE_NAME;
393        }
394        if (title == null) {
395            title = icon;
396            titleSource = JFCComponentTitleHierachy.ICON;
397        }
398        if (title == null) {
399            title = component.getName();
400            titleSource = JFCComponentTitleHierachy.NAME;
401        }
402        if (title == null) {
403            // use coordinates as last resort
404            title = "Pos(" + component.getX() + "," + component.getY() + ")";
405            titleSource = JFCComponentTitleHierachy.POS;
406        }
407    }
408
409    /**
410     * <p>
411     * Sets the {@link #componentClass} of the component.
412     * </p>
413     */
414    private void setClass() {
415        componentClass = component.getClass().getName();
416    }
417
418    /**
419     * <p>
420     * Sets the {@link #icon} of the component.
421     * </p>
422     */
423    private void setIcon() {
424        icon = null; // reset icon
425
426        Method getIconMethod;
427        try {
428            getIconMethod = component.getClass().getMethod("getIcon", new Class[0]);
429            if (getIconMethod != null) {
430                Object iconObject = getIconMethod.invoke(component, new Object[] { });
431                if (iconObject != null) {
432                    String iconPath = iconObject.toString();
433                    if (!iconPath.contains("@")) {
434                        System.out.println("iconPath");
435                        String[] splitResult =
436                            iconPath.split(File.separatorChar == '\\' ? "\\\\" : File.separator);
437                        icon = splitResult[splitResult.length - 1];
438                    }
439                }
440            }
441        }
442        catch (SecurityException e) {}
443        catch (NoSuchMethodException e) {}
444        catch (IllegalArgumentException e) {}
445        catch (IllegalAccessException e) {}
446        catch (InvocationTargetException e) {
447            System.err.println("Found method with name " + "getIcon" + " but could not access it.");
448        }
449    }
450
451    /**
452     * <p>
453     * Sets the {@link #index} of the component as the index in the parent, if it is accessible.
454     * </p>
455     */
456    private void setIndex() {
457        index = -1; // reset index
458
459        AccessibleContext accessibleContext = component.getAccessibleContext();
460        if (accessibleContext != null) {
461            index = accessibleContext.getAccessibleIndexInParent();
462        }
463    }
464   
465    /**
466     * <p>
467     * Constructs a string that represents inheritance of component.
468     * </p>
469     * @return
470     */
471    private String getClassHierarchy(){
472        StringBuilder builder = new StringBuilder();
473        Class<? extends Object> classobject = component.getClass();
474        builder.append(" <ancestors>" + StringTools.ENDLINE);
475        while(classobject != null){
476                builder.append("  <ancestor name=\"");
477                builder.append(classobject.getName());
478                builder.append("\" />" + StringTools.ENDLINE);
479                classobject = classobject.getSuperclass();
480        }
481        builder.append(" </ancestors>" + StringTools.ENDLINE);
482        return builder.toString();
483    }
484}
Note: See TracBrowser for help on using the repository browser.