// 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.plugin.mfc.guimodel; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementFactory; import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel; import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException; import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementFactory; /** *

* This class provides an the interfaces for window trees. *

*

* The window tree represents the hierarchical structure of the windows "as it is" currently during * a session. It may change during the session due to creation and destruction of windows. *

* * @author Steffen Herbold * @version 1.0 */ public class WindowTree { /** *

* Maintains a set of all the targets of all widgets that were at some point part of the window * tree. *

*/ private Set targets; /** *

* Map of all GUI element specifications that are part of the tree for efficient searching. The * keys of the map are the hwnd's of the GUI elements. *

*/ private Map guiElementSpecs; /** *

* Map of all children of GUI elements that are part of the tree. The keys of the map are the * hwnd's of the parent GUI elements. *

*/ private Map> childRelations; /** *

* Map of all parents of GUI elements that are part of the tree. The keys of the map are the * hwnd's of the child GUI elements. *

*/ private Map parentRelations; /** *

* the internally created GUI model *

*/ private GUIModel guiModel = new GUIModel(); /** *

* the GUI element factory used in the model *

*/ private IGUIElementFactory guiElementFactory = GUIElementFactory.getInstance(); /** *

* Map of all GUI elements that are part of the tree for efficient searching. The keys of the * map are the hwnd's of the GUI elements. *

*/ private Map guiElements; /** *

* Creates a new WindowTree. *

*

* Private, as the class is a singleton. *

*/ public WindowTree() { guiElementSpecs = new HashMap(); targets = new HashSet(); childRelations = new HashMap>(); parentRelations = new HashMap(); guiElements = new HashMap(); } /** *

* Adds a new window to the tree. *

* * @param parentHwnd * hwnd of the parent window * @param childHwnd * hwnd of the window to be created * @param childWindowName * resource id of the window to be created * @param resourceId * resource id of the window to be created * @param className * class name of the window to be created */ public void add(long parentHwnd, long childHwnd, String childWindowName, int resourceId, String className, boolean isModal) { MFCGUIElementSpec parent = guiElementSpecs.get(parentHwnd); MFCGUIElementSpec child = guiElementSpecs.get(childHwnd); if (child == null) { child = new MFCGUIElementSpec(childHwnd, childWindowName, resourceId, className, isModal); if (parent != null) { List otherChildren = childRelations.get(parentHwnd); if (otherChildren == null) { otherChildren = new ArrayList(); childRelations.put(parentHwnd, otherChildren); } otherChildren.add(child); parentRelations.put(childHwnd, parent); } guiElementSpecs.put(childHwnd, child); targets.add(child); } } /** *

* Searches the tree for a window with the specified hwnd and returns its * {@link MFCGUIElementSpec} . *

* * @param hwnd * hwnd that is looked for * @return {@link MFCGUIElementSpec} of the window with the given hwnd if found, null otherwise */ public MFCGUIElement find(long hwnd) { MFCGUIElement guiElement = guiElements.get(hwnd); if (guiElement == null) { List guiElementPath = new ArrayList(); MFCGUIElementSpec child = guiElementSpecs.get(hwnd); if (child == null) { throw new RuntimeException("no GUI element found with id " + hwnd); } while (child != null) { guiElementPath.add(0, child); child = parentRelations.get(child.getHwnd()); } try { guiElement = (MFCGUIElement) guiModel.integratePath(guiElementPath, guiElementFactory); } catch (GUIModelException e) { throw new RuntimeException("could not instantiate GUI element with id " + hwnd, e); } guiElements.put(hwnd, guiElement); } return guiElement; } /** *

* Sets the name of a GUI element given its HWND. *

* * @param hwnd * HWND of the GUI element * @param windowName * new name of the GUI element */ public void setName(long hwnd, String windowName) { MFCGUIElementSpec child = guiElementSpecs.get(hwnd); if (child != null) { child.setName(windowName); MFCGUIElement guiElement = guiElements.remove(hwnd); if (guiElement == null) { // we need to update the GUI model as well find(hwnd); } } } /** *

* Removes a window (defined by its hwnd) from the tree. All children of the window will be * removed recursively. *

* * @param hwnd * hwnd of the window to be removed * @return number of windows that were removed */ public int remove(long hwnd) { MFCGUIElementSpec node = guiElementSpecs.remove(hwnd); int removedCounter = 1; if (node != null) { List nodesToBeRemoved = childRelations.remove(hwnd); // remove all children and sub-children, if any if (nodesToBeRemoved != null) { for (int i = 0; i < nodesToBeRemoved.size(); i++) { MFCGUIElementSpec nodeToBeRemoved = nodesToBeRemoved.get(i); List children = childRelations.remove(nodeToBeRemoved.getHwnd()); if (children != null) { nodesToBeRemoved.addAll(children); } guiElementSpecs.remove(nodeToBeRemoved.getHwnd()); parentRelations.remove(nodeToBeRemoved.getHwnd()); removedCounter++; } } // the node may be a child node of a parent. So search for it and remove it MFCGUIElementSpec parent = parentRelations.remove(hwnd); if (parent != null) { List children = childRelations.get(parent.getHwnd()); if (children != null) { for (int i = 0; i < children.size(); i++) { if (children.get(i).getHwnd() == hwnd) { children.remove(i); break; } } if (children.size() <= 0) { childRelations.remove(parent.getHwnd()); } } } } return removedCounter; } /** * @return the guiModel */ public GUIModel getGUIModel() { return guiModel; } /** *

* Returns the number of nodes contained in the WindowTree. *

* * @return number of nodes */ public int size() { return guiElementSpecs.size(); } /** *

* Returns a sorted set of all targets that existed any time in the window tree. *

* * @return set of targets */ public Set getTargets() { return targets; } }