// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.eventcore.guimodel; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; /** *

* Skeletal implementation for GUI elements. *

* * @version 1.0 * @author Patrick Harms */ public abstract class AbstractDefaultGUIElement implements IGUIElement { /** *

* Id for object serialization. *

*/ public static final long serialVersionUID = 1L; /** *

* The reference to equal GUI element manager (needed to preserve singleton behavior, even * though the objects are not singleton). *

*/ private static final EqualGUIElementManager equalGUIElementManager = new EqualGUIElementManager(); /** *

* Specification of the GUI element *

*/ private final IGUIElementSpec specification; /** *

* Reference to the parent element *

*/ private final IGUIElement parent; /** *

* Boolean that indicates if a GUIElement was used during a session. *

*/ boolean usageObserved; /** *

* Constructor. Creates a new AbstractDefaultGUIElement. *

* * @param specification * specification of the created GUI element * @param parent * parent of the created GUI element; null means the element is a top-level window */ public AbstractDefaultGUIElement(IGUIElementSpec specification, IGUIElement parent) { this.specification = specification; this.parent = parent; this.usageObserved = false; } /* * (non-Javadoc) * * @see de.ugoe.cs.tasktree.guimodel.GUIElement#getSpecification() */ @Override public IGUIElementSpec getSpecification() { return specification; } /* * (non-Javadoc) * * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#getParent() */ @Override public IGUIElement getParent() { return parent; } /* * (non-Javadoc) * * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#addEqualGUIElement(IGUIElement) */ @Override public void addEqualGUIElement(IGUIElement equalElement) { equalGUIElementManager.addEqualGUIElements(this, equalElement); } /* * (non-Javadoc) * * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#isUsed() */ @Override public boolean isUsed() { return usageObserved; } /* * (non-Javadoc) * * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#markUsed() */ @Override public void markUsed() { this.usageObserved = true; } /* * (non-Javadoc) * * @see GUIElement#equals(GUIElement) */ public final boolean equals(Object other) { // implement final, as GUI elements are all singletons and they equal only if they are the // same object or if they are in the list of equal GUI elements return super.equals(other) || equalGUIElementManager.equals(this, other); } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public final int hashCode() { // implement final, as GUI elements are all singletons and they equal only if they are the // same object. If there are several GUI element objects that represent the same GUI element // then they are stored in the list of equal elements. In this case, the hash code of the // list is unique within the system return equalGUIElementManager.hashCode(this); } /** *

* This internal helper class manages equal GUI elements. This is necessary, as we often first * identify many GUI elements as different and later use a heuristic to determine that they are * the same. This class provides the means to preserve the singleton behavior of the GUI * elements after we merge two GUI elements. *

* * @version 1.0 * @author Patrick Harms */ private static class EqualGUIElementManager { /** *

* The internal map of GUI elements mapping each registered element to its equal variants. * We use the {@link IdentityHashMap} as a normal hash map would not work because of the * changing of the hash code of GUI elements at runtime. *

*/ private IdentityHashMap> identityHashMap = new IdentityHashMap>(); /** *

* Adds a new equals relationship between two {@link IGUIElement}s to the equality manager. *

* * @param guiElement1 * first equal GUI element * @param guiElement2 * second equal GUI element */ private synchronized void addEqualGUIElements(IGUIElement guiElement1, IGUIElement guiElement2) { List list1 = identityHashMap.get(guiElement1); List list2 = identityHashMap.get(guiElement2); if (list1 == null) { if (list2 == null) { list2 = new LinkedList(); list2.add(guiElement1); list2.add(guiElement2); identityHashMap.put(guiElement1, list2); identityHashMap.put(guiElement2, list2); } else { addIfNotContained(list2, guiElement1); identityHashMap.put(guiElement1, list2); } } else { if (list2 == null) { addIfNotContained(list1, guiElement2); identityHashMap.put(guiElement2, list1); } else if (list1 != list2) { list1.addAll(list2); identityHashMap.put(guiElement2, list1); // we also have to set this new list for all other elements for which so // far list2 was registered for (IGUIElement candidate : list2) { identityHashMap.put(candidate, list1); } } // else // in this case, both GUI elements should already be registered with the same // lists. } } /** *

* Returns the object hash of a {@link IGUIElement}. *

* * @param guiElement * gui element whose object hash is determined * @return the object hash */ private synchronized int hashCode(IGUIElement guiElement) { return System.identityHashCode(getEqualElementsList(guiElement)); } /** *

* Determines the equality of two {@link IGUIElement}s based on the information of the * identify manager. Two elements are equal, if they have been added as equal using * {@link #addEqualGUIElements(IGUIElement, IGUIElement)}. *

* * @param guiElement * GUI element to which the object is compared * @param other * object that is compared to the GUI element * @return */ private synchronized boolean equals(IGUIElement guiElement, Object other) { if (other instanceof IGUIElement) { for (IGUIElement candidate : getEqualElementsList(guiElement)) { if (candidate == other) { return true; } } } return false; } /** *

* Returns the equal {@link IGUIElement} of a given {@link IGUIElement}. *

* * @param guiElement * GUI element of which the equal elements are returned * @return the equal GUI elements */ private List getEqualElementsList(IGUIElement guiElement) { List returnValue = identityHashMap.get(guiElement); if (returnValue == null) { returnValue = new LinkedList(); returnValue.add(guiElement); identityHashMap.put(guiElement, returnValue); } return returnValue; } /** *

* Adds {@link IGUIElement} as equal to a list of {@link IGUIElement} if and only if it is * not already contained. *

* * @param equalElementsList * list of {@link IGUIElement} to which the GUI element is added * @param guiElement * GUI element to be added */ private void addIfNotContained(List equalElementsList, IGUIElement guiElement) { for (IGUIElement candidate : equalElementsList) { if (candidate == guiElement) { return; } } equalElementsList.add(guiElement); } } }