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

Last change on this file since 1046 was 1046, checked in by adeicke, 11 years ago

Fixing an issue, where unescaped text is stored in the logfile.

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