source: trunk/quest-jfcmonitor/src/main/java/de/ugoe/cs/quest/jfcmonitor/JFCComponent.java @ 854

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