source: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/AbstractDefaultGUIElement.java @ 1116

Last change on this file since 1116 was 1115, checked in by pharms, 11 years ago
  • corrected equality handling of GUI elements
  • Property svn:executable set to *
File size: 10.2 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.eventcore.guimodel;
16
17import java.util.IdentityHashMap;
18import java.util.LinkedList;
19import java.util.List;
20
21/**
22 * <p>
23 * Skeletal implementation for GUI elements.
24 * </p>
25 *
26 * @version 1.0
27 * @author Patrick Harms
28 */
29public abstract class AbstractDefaultGUIElement implements IGUIElement {
30
31    /**
32     * <p>
33     * Id for object serialization.
34     * </p>
35     */
36    public static final long serialVersionUID = 1L;
37
38    /**
39     * <p>
40     * The reference to equal GUI element manager (needed to preserve singleton behavior, even
41     * though the objects are not singleton).
42     * </p>
43     */
44    private static final EqualGUIElementManager equalGUIElementManager =
45        new EqualGUIElementManager();
46
47    /**
48     * <p>
49     * Specification of the GUI element
50     * </p>
51     */
52    private final IGUIElementSpec specification;
53
54    /**
55     * <p>
56     * Reference to the parent element
57     * </p>
58     */
59    private final IGUIElement parent;
60   
61    /**
62     * <p>
63     * Boolean that indicates if a GUIElement was used during a session.
64     * </p>
65     */
66    boolean usageObserved;
67
68    /**
69     * <p>
70     * Constructor. Creates a new AbstractDefaultGUIElement.
71     * </p>
72     *
73     * @param specification
74     *            specification of the created GUI element
75     * @param parent
76     *            parent of the created GUI element; null means the element is a top-level window
77     */
78    public AbstractDefaultGUIElement(IGUIElementSpec specification, IGUIElement parent) {
79        this.specification = specification;
80        this.parent = parent;
81        this.usageObserved = false;
82    }
83
84    /*
85     * (non-Javadoc)
86     *
87     * @see de.ugoe.cs.tasktree.guimodel.GUIElement#getSpecification()
88     */
89    @Override
90    public IGUIElementSpec getSpecification() {
91        return specification;
92    }
93
94    /*
95     * (non-Javadoc)
96     *
97     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#getParent()
98     */
99    @Override
100    public IGUIElement getParent() {
101        return parent;
102    }
103
104    /*
105     * (non-Javadoc)
106     *
107     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#addEqualGUIElement(IGUIElement)
108     */
109    @Override
110    public void addEqualGUIElement(IGUIElement equalElement) {
111        equalGUIElementManager.addEqualGUIElements(this, equalElement);
112    }
113
114    /*
115     * (non-Javadoc)
116     *
117     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#isUsed()
118     */
119    @Override
120    public boolean isUsed() {
121        return usageObserved;
122    }
123
124    /*
125     * (non-Javadoc)
126     *
127     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#markUsed()
128     */
129    @Override
130    public void markUsed() {
131        this.usageObserved = true;
132    }
133
134    /*
135     * (non-Javadoc)
136     *
137     * @see GUIElement#equals(GUIElement)
138     */
139    public final boolean equals(Object other) {
140        // implement final, as GUI elements are all singletons and they equal only if they are the
141        // same object or if they are in the list of equal GUI elements
142        return super.equals(other) || equalGUIElementManager.equals(this, other);
143    }
144
145    /*
146     * (non-Javadoc)
147     *
148     * @see java.lang.Object#hashCode()
149     */
150    @Override
151    public final int hashCode() {
152        // implement final, as GUI elements are all singletons and they equal only if they are the
153        // same object. If there are several GUI element objects that represent the same GUI element
154        // then they are stored in the list of equal elements. In this case, the hash code of the
155        // list is unique within the system
156        return equalGUIElementManager.hashCode(this);
157    }
158   
159    /**
160     * <p>
161     * This internal helper class manages equal GUI elements. This is necessary, as we often first
162     * identify many GUI elements as different and later use a heuristic to determine that they are
163     * the same. This class provides the means to preserve the singleton behavior of the GUI
164     * elements after we merge two GUI elements.
165     * </p>
166     *
167     * @version 1.0
168     * @author Patrick Harms
169     */
170    private static class EqualGUIElementManager {
171
172        /**
173         * <p>
174         * The internal map of GUI elements mapping each registered element to its equal variants.
175         * We use the {@link IdentityHashMap} as a normal hash map would not work because of the
176         * changing of the hash code of GUI elements at runtime.
177         * </p>
178         */
179        private IdentityHashMap<IGUIElement, List<IGUIElement>> identityHashMap =
180            new IdentityHashMap<IGUIElement, List<IGUIElement>>();
181
182        /**
183         * <p>
184         * Adds a new equals relationship between two {@link IGUIElement}s to the equality manager.
185         * </p>
186         *
187         * @param guiElement1
188         *            first equal GUI element
189         * @param guiElement2
190         *            second equal GUI element
191         */
192        private synchronized void addEqualGUIElements(IGUIElement guiElement1,
193                                                      IGUIElement guiElement2)
194        {
195            List<IGUIElement> list1 = identityHashMap.get(guiElement1);
196            List<IGUIElement> list2 = identityHashMap.get(guiElement2);
197
198            if (list1 == null) {
199                if (list2 == null) {
200                    list2 = new LinkedList<IGUIElement>();
201                    list2.add(guiElement1);
202                    list2.add(guiElement2);
203                    identityHashMap.put(guiElement1, list2);
204                    identityHashMap.put(guiElement2, list2);
205                }
206                else {
207                    addIfNotContained(list2, guiElement1);
208                    identityHashMap.put(guiElement1, list2);
209                }
210            }
211            else {
212                if (list2 == null) {
213                    addIfNotContained(list1, guiElement2);
214                    identityHashMap.put(guiElement2, list1);
215                }
216                else if (list1 != list2) {
217                    list1.addAll(list2);
218                    identityHashMap.put(guiElement2, list1);
219                   
220                    // we also have to set this new list for all other elements for which so
221                    // far list2 was registered
222                    for (IGUIElement candidate : list2) {
223                        identityHashMap.put(candidate, list1);
224                    }
225                }
226                // else
227                // in this case, both GUI elements should already be registered with the same
228                // lists.
229            }
230        }
231
232        /**
233         * <p>
234         * Returns the object hash of a {@link IGUIElement}.
235         * </p>
236         *
237         * @param guiElement
238         *            gui element whose object hash is determined
239         * @return the object hash
240         */
241        private synchronized int hashCode(IGUIElement guiElement) {
242            return System.identityHashCode(getEqualElementsList(guiElement));
243        }
244
245        /**
246         * <p>
247         * Determines the equality of two {@link IGUIElement}s based on the information of the
248         * identify manager. Two elements are equal, if they have been added as equal using
249         * {@link #addEqualGUIElements(IGUIElement, IGUIElement)}.
250         * </p>
251         *
252         * @param guiElement
253         *            GUI element to which the object is compared
254         * @param other
255         *            object that is compared to the GUI element
256         * @return
257         */
258        private synchronized boolean equals(IGUIElement guiElement, Object other) {
259            if (other instanceof IGUIElement) {
260                for (IGUIElement candidate : getEqualElementsList(guiElement)) {
261                    if (candidate == other) {
262                        return true;
263                    }
264                }
265            }
266
267            return false;
268        }
269
270        /**
271         * <p>
272         * Returns the equal {@link IGUIElement} of a given {@link IGUIElement}.
273         * </p>
274         *
275         * @param guiElement
276         *            GUI element of which the equal elements are returned
277         * @return the equal GUI elements
278         */
279        private List<IGUIElement> getEqualElementsList(IGUIElement guiElement) {
280            List<IGUIElement> returnValue = identityHashMap.get(guiElement);
281
282            if (returnValue == null) {
283                returnValue = new LinkedList<IGUIElement>();
284                returnValue.add(guiElement);
285                identityHashMap.put(guiElement, returnValue);
286            }
287
288            return returnValue;
289        }
290
291        /**
292         * <p>
293         * Adds {@link IGUIElement} as equal to a list of {@link IGUIElement} if and only if it is
294         * not already contained.
295         * </p>
296         *
297         * @param equalElementsList
298         *            list of {@link IGUIElement} to which the GUI element is added
299         * @param guiElement
300         *            GUI element to be added
301         */
302        private void addIfNotContained(List<IGUIElement> equalElementsList, IGUIElement guiElement)
303        {
304            for (IGUIElement candidate : equalElementsList) {
305                if (candidate == guiElement) {
306                    return;
307                }
308            }
309
310            equalElementsList.add(guiElement);
311        }
312
313    }
314
315}
Note: See TracBrowser for help on using the repository browser.