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

Last change on this file since 927 was 927, checked in by sherbold, 12 years ago
  • added copyright under the Apache License, Version 2.0
  • Property svn:executable set to *
File size: 9.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     * Constructor. Creates a new AbstractDefaultGUIElement.
64     * </p>
65     *
66     * @param specification
67     *            specification of the created GUI element
68     * @param parent
69     *            parent of the created GUI element; null means the element is a top-level window
70     */
71    public AbstractDefaultGUIElement(IGUIElementSpec specification, IGUIElement parent) {
72        this.specification = specification;
73        this.parent = parent;
74    }
75
76    /*
77     * (non-Javadoc)
78     *
79     * @see de.ugoe.cs.tasktree.guimodel.GUIElement#getSpecification()
80     */
81    @Override
82    public IGUIElementSpec getSpecification() {
83        return specification;
84    }
85
86    /*
87     * (non-Javadoc)
88     *
89     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#getParent()
90     */
91    @Override
92    public IGUIElement getParent() {
93        return parent;
94    }
95
96    /*
97     * (non-Javadoc)
98     *
99     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#addEqualGUIElement(IGUIElement)
100     */
101    @Override
102    public void addEqualGUIElement(IGUIElement equalElement) {
103        equalGUIElementManager.addEqualGUIElements(this, equalElement);
104    }
105
106    /*
107     * (non-Javadoc)
108     *
109     * @see GUIElement#equals(GUIElement)
110     */
111    public final boolean equals(Object other) {
112        // implement final, as GUI elements are all singletons and they equal only if they are the
113        // same object or if they are in the list of equal GUI elements
114        return super.equals(other) || equalGUIElementManager.equals(this, other);
115    }
116
117    /*
118     * (non-Javadoc)
119     *
120     * @see java.lang.Object#hashCode()
121     */
122    @Override
123    public final int hashCode() {
124        // implement final, as GUI elements are all singletons and they equal only if they are the
125        // same object. If there are several GUI element objects that represent the same GUI element
126        // then they are stored in the list of equal elements. In this case, the hash code of the
127        // list is unique within the system
128        return equalGUIElementManager.hashCode(this);
129    }
130
131    /**
132     * <p>
133     * This internal helper class manages equal GUI elements. This is necessary, as we often first
134     * identify many GUI elements as different and later use a heuristic to determine that they are
135     * the same. This class provides the means to preserve the singleton behavior of the GUI
136     * elements after we merge two GUI elements.
137     * </p>
138     *
139     * @version 1.0
140     * @author Patrick Harms
141     */
142    private static class EqualGUIElementManager {
143
144        /**
145         * <p>
146         * The internal map of GUI elements mapping each registered element to its equal variants.
147         * We use the {@link IdentityHashMap} as a normal hash map would not work because of the
148         * changing of the hash code of GUI elements at runtime.
149         * </p>
150         */
151        private IdentityHashMap<IGUIElement, List<IGUIElement>> identityHashMap =
152            new IdentityHashMap<IGUIElement, List<IGUIElement>>();
153
154        /**
155         * <p>
156         * Adds a new equals relationship between two {@link IGUIElement}s to the equality manager.
157         * </p>
158         *
159         * @param guiElement1
160         *            first equal GUI element
161         * @param guiElement2
162         *            second equal GUI element
163         */
164        private synchronized void addEqualGUIElements(IGUIElement guiElement1,
165                                                      IGUIElement guiElement2)
166        {
167            List<IGUIElement> list1 = identityHashMap.get(guiElement1);
168            List<IGUIElement> list2 = identityHashMap.get(guiElement2);
169
170            if (list1 == null) {
171                if (list2 == null) {
172                    list2 = new LinkedList<IGUIElement>();
173                    list2.add(guiElement1);
174                    list2.add(guiElement2);
175                    identityHashMap.put(guiElement1, list2);
176                    identityHashMap.put(guiElement2, list2);
177                }
178                else {
179                    addIfNotContained(list2, guiElement1);
180                    identityHashMap.put(guiElement1, list2);
181                }
182            }
183            else {
184                if (list2 == null) {
185                    addIfNotContained(list1, guiElement2);
186                    identityHashMap.put(guiElement2, list1);
187                }
188                else if (list1 != list2) {
189                    list1.addAll(list2);
190                    identityHashMap.put(guiElement2, list1);
191                }
192                // else
193                // in this case, both GUI elements should already be registered with the same
194                // lists.
195            }
196        }
197
198        /**
199         * <p>
200         * Returns the object hash of a {@link IGUIElement}.
201         * </p>
202         *
203         * @param guiElement
204         *            gui element whose object hash is determined
205         * @return the object hash
206         */
207        private synchronized int hashCode(IGUIElement guiElement) {
208            return System.identityHashCode(getEqualElementsList(guiElement));
209        }
210
211        /**
212         * <p>
213         * Determines the equality of two {@link IGUIElement}s based on the information of the
214         * identify manager. Two elements are equal, if they have been added as equal using
215         * {@link #addEqualGUIElements(IGUIElement, IGUIElement)}.
216         * </p>
217         *
218         * @param guiElement
219         *            GUI element to which the object is compared
220         * @param other
221         *            object that is compared to the GUI element
222         * @return
223         */
224        private synchronized boolean equals(IGUIElement guiElement, Object other) {
225            if (other instanceof IGUIElement) {
226                for (IGUIElement candidate : getEqualElementsList(guiElement)) {
227                    if (candidate == other) {
228                        return true;
229                    }
230                }
231            }
232
233            return false;
234        }
235
236        /**
237         * <p>
238         * Returns the equal {@link IGUIElement} of a given {@link IGUIElement}.
239         * </p>
240         *
241         * @param guiElement
242         *            GUI element of which the equal elements are returned
243         * @return the equal GUI elements
244         */
245        private List<IGUIElement> getEqualElementsList(IGUIElement guiElement) {
246            List<IGUIElement> returnValue = identityHashMap.get(guiElement);
247
248            if (returnValue == null) {
249                returnValue = new LinkedList<IGUIElement>();
250                returnValue.add(guiElement);
251                identityHashMap.put(guiElement, returnValue);
252            }
253
254            return returnValue;
255        }
256
257        /**
258         * <p>
259         * Adds {@link IGUIElement} as equal to a list of {@link IGUIElement} if and only if it is
260         * not already contained.
261         * </p>
262         *
263         * @param equalElementsList
264         *            list of {@link IGUIElement} to which the GUI element is added
265         * @param guiElement
266         *            GUI element to be added
267         */
268        private void addIfNotContained(List<IGUIElement> equalElementsList, IGUIElement guiElement)
269        {
270            for (IGUIElement candidate : equalElementsList) {
271                if (candidate == guiElement) {
272                    return;
273                }
274            }
275
276            equalElementsList.add(guiElement);
277        }
278
279    }
280
281}
Note: See TracBrowser for help on using the repository browser.