package de.ugoe.cs.quest.plugin.mfc.guimodel; import java.util.ArrayList; import java.util.List; import de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec; import de.ugoe.cs.util.StringTools; /** *

* This class implements a node in the {@link WindowTree} that is maintained during parsing a * session. *

*

* The window tree is structure that contains the hierarchy of the windows of a application as well * as basic information about each window: the hwnd; its name; its resource id; its class name. *

* * @author Steffen Herbold * @version 1.0 */ public class MFCGUIElementSpec implements IGUIElementSpec { /** *

* current name of the window *

*/ private String name; /** *

* previous names of the window as it may have changed over time. *

*/ private List formerNames = new ArrayList(); /** *

* Handle of the window. Used as unique identifier during its existence. *

*/ private long hwnd; /** *

* previous handles of the window as the window may have been destroyed and recreated *

*/ private List formerHwnds = new ArrayList(); /** *

* Resource id of the window. *

*/ private final int resourceId; /** *

* type (class name) of the window. *

*/ private final String type; /** *

* True, if the window is modal. *

*/ private final boolean isModal; /** *

* Creates a new WindowTreeNode. *

*

* The constructor is protected WindowTreeNode may only be created from the WindowTree. *

* * @param hwnd * hwnd of the window * @param parent * reference to the parent's WindowTreeNode * @param name * name of the window * @param resourceId * resource id of the window * @param type * type, i.e. class name of the window * @param isModal * modality of the window */ protected MFCGUIElementSpec(long hwnd, String name, int resourceId, String type, boolean isModal) { this.hwnd = hwnd; this.name = name; this.resourceId = resourceId; this.type = type; this.isModal = isModal; } /** *

* Returns the name of the window. *

* * @return name of the window */ public String getName() { StringBuffer names = new StringBuffer(); if (name != null) { names.append('"'); names.append(name); names.append('"'); } else { names.append("NOT_SET"); } if (formerNames.size() > 0) { names.append(" (aka "); for (int i = 0; i < formerNames.size(); i++) { if (i > 0) { names.append("/"); } names.append('"'); names.append(formerNames.get(i)); names.append('"'); } names.append(")"); } return names.toString(); } /** *

* Returns the hwnd of the window. *

* * @return hwnd of the window */ public long getHwnd() { return hwnd; } /** *

* Returns the resource id of the window. *

* * @return resource id of the window */ public int getResourceId() { return resourceId; } /* (non-Javadoc) * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getType() */ @Override public String getType() { return type; } /** *

* TODO: comment *

* * @return */ public boolean isModal() { return isModal; } /** *

* Sets the name of the window. *

* * @param text * new name of the window */ public void setName(String newName) { if ((this.name != null) && (!this.name.equals(newName)) && (!this.formerNames.contains(this.name))) { this.formerNames.add(this.name); } this.name = newName; } /** *

* Sets the hwnd of the window. *

* * @param text * new name of the window */ public void setHwnd(long newHwnd) { if (!this.formerHwnds.contains(this.hwnd)) { this.formerHwnds.add(this.hwnd); } this.hwnd = newHwnd; } /* (non-Javadoc) * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getSimilarity(de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec) */ @Override public boolean getSimilarity(IGUIElementSpec other) { if (this == other) { return true; } if (!(other instanceof MFCGUIElementSpec)) { return false; } MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other; if ((type != otherSpec.type) && ((type != null) && (!type.equals(otherSpec.type)))) { return false; } if (isModal != otherSpec.isModal) { return false; } if (resourceId != otherSpec.resourceId) { return false; } // up to now, we compared, if the basics match. Now lets compare the id and the // name. Both may change. The name may be reset (e.g. the title of a frame using the // asterisk in the case data was changed). The id may change if e.g. a dialog is closed // and reopend, i.e. a new instance is created. If one of them stays the same, then // similarity is given. Therefore these are the first two comparisons if (hwnd == otherSpec.hwnd) { return true; } if ((name != null) && (name.equals(otherSpec.name))) { return true; } if ((((name == null) && (otherSpec.name == null)) || (("".equals(name)) && ("".equals(otherSpec.name)))) && (formerNames.size() == 0) && (otherSpec.formerNames.size() == 0)) { return true; } // if the hwnd and the name did not stay the same, then the name should be checked first. // The current name of one of the specs must be contained in the former names of the // respective other spec for similarity. Either of the specs should contain the name of the // respective other spec in its former names. We can rely on this, as in the MFC context // we get to know each name change. I.e. although currently the names of the specs differ, // once they were identical. But it is sufficient to do it for the current names of the // elements, as only one of them may have experienced more name changes then the other. if ((otherSpec.name != null) && formerNames.contains(otherSpec.name)) { return true; } if ((name != null) && otherSpec.formerNames.contains(name)) { return true; } // ok. Even the names do not match. This is usually a clear indication, that the elements // are distinct. However, we check, if the former handles matched. This is very unlikely // to happen. But it may occur, if a GUI element does not have a name or its name stays // the empty string and if this GUI element is created, destroyed, and created again. if (formerHwnds.contains(otherSpec.hwnd) || otherSpec.formerHwnds.contains(hwnd)) { return true; } // now we can be really sure, that the GUI elements differ return false; } /* (non-Javadoc) * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#equals(IGUIElementSpec) */ @Override public boolean equals(IGUIElementSpec other) { if (this == other) { return true; } if (!(other instanceof MFCGUIElementSpec)) { return false; } MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other; return (hwnd == otherSpec.hwnd) && (isModal == otherSpec.isModal) && (resourceId == otherSpec.resourceId) && ((type == otherSpec.type) || ((type != null) && (type.equals(otherSpec.type)))) && ((name == otherSpec.name) || ((name != null) && (name.equals(otherSpec.name)))); } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { // reuse only invariable elements return (type + isModal + resourceId).hashCode(); } /** *

* Returns a string identfier of the window:
* {@code [resourceId;"windowName";"className";modality]} *

* * @return identifier string of the window */ @Override public String toString() { return "[" + resourceId + ";" + getName() + ";\"" + type + "\";" + isModal + ";" + hwnd + "]"; } /** *

* TODO: comment *

*/ String toXML() { return ""; } /** *

* TODO: comment *

* * @param furtherSpec */ void update(IGUIElementSpec furtherSpec) { MFCGUIElementSpec other = (MFCGUIElementSpec) furtherSpec; if (other != this) { for (long formerHwnd : other.formerHwnds) { setHwnd(formerHwnd); } if (hwnd != other.hwnd) { hwnd = other.hwnd; } for (String formerName : other.formerNames) { setName(formerName); } if ((name != other.name) && (name != null) && (!name.equals(other.name))) { setName(other.name); } } } }