Index: trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/guimodel/MFCGUIElementSpec.java
===================================================================
--- trunk/quest-ui-core/src/main/java/de/ugoe/cs/quest/plugin/mfc/eventcore/WindowTreeNode.java	(revision 488)
+++ trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/guimodel/MFCGUIElementSpec.java	(revision 619)
@@ -1,18 +1,19 @@
-package de.ugoe.cs.quest.plugin.mfc.eventcore;
+
+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;
 
 /**
  * <p>
- * This class implements a node in the {@link WindowTree} that is maintained
- * during parsing a session.
+ * This class implements a node in the {@link WindowTree} that is maintained during parsing a
+ * session.
  * </p>
  * <p>
- * 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.
+ * 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.
  * </p>
  * 
@@ -20,273 +21,372 @@
  * @version 1.0
  */
-public class WindowTreeNode {
-
-	/**
-	 * <p>
-	 * Name of the window. May change over time.
-	 * </p>
-	 */
-	private String windowName;
-
-	/**
-	 * <p>
-	 * Handle of the window. Used as unique identifier during its existence.
-	 * </p>
-	 */
-	private final int hwnd;
-
-	/**
-	 * <p>
-	 * Resource id of the window.
-	 * </p>
-	 */
-	private final int resourceId;
-
-	/**
-	 * <p>
-	 * Class name of the window.
-	 * </p>
-	 */
-	private final String className;
-
-	/**
-	 * <p>
-	 * True, if the window is modal.
-	 * </p>
-	 */
-	private final boolean isModal;
-
-	/**
-	 * <p>
-	 * Parent of the window. <code>null</code> if the window has no parent.
-	 * </p>
-	 */
-	private WindowTreeNode parent;
-
-	/**
-	 * <p>
-	 * List of the windows children. May be empty.
-	 * </p>
-	 */
-	private List<WindowTreeNode> children;
-
-	/**
-	 * <p>
-	 * Creates a new WindowTreeNode.
-	 * </p>
-	 * <p>
-	 * The constructor is protected WindowTreeNode may only be created from the
-	 * WindowTree.
-	 * </p>
-	 * 
-	 * @param hwnd
-	 *            hwnd of the window
-	 * @param parent
-	 *            reference to the parent's WindowTreeNode
-	 * @param windowName
-	 *            name of the window
-	 * @param resourceId
-	 *            resource id of the window
-	 * @param className
-	 *            class name of the window
-	 * @param isModal
-	 *            modality of the window
-	 */
-	protected WindowTreeNode(int hwnd, WindowTreeNode parent,
-			String windowName, int resourceId, String className, boolean isModal) {
-		this.hwnd = hwnd;
-		this.parent = parent;
-		this.windowName = windowName;
-		this.resourceId = resourceId;
-		this.className = className;
-		this.isModal = isModal;
-		children = new ArrayList<WindowTreeNode>();
-	}
-
-	/**
-	 * <p>
-	 * Returns a reference to the WindowTreeNode of the parent.
-	 * </p>
-	 * 
-	 * @return WindowTreeNode of the parent
-	 */
-	public WindowTreeNode getParent() {
-		return parent;
-	}
-
-	/**
-	 * <p>
-	 * Returns the list of the windows children.
-	 * </p>
-	 * 
-	 * @return list of the windows children
-	 */
-	public List<WindowTreeNode> getChildren() {
-		return children;
-	}
-
-	/**
-	 * <p>
-	 * Returns the name of the window.
-	 * </p>
-	 * 
-	 * @return name of the window
-	 */
-	public String getName() {
-		return windowName;
-	}
-
-	/**
-	 * <p>
-	 * Returns the hwnd of the window.
-	 * </p>
-	 * 
-	 * @return hwnd of the window
-	 */
-	public int getHwnd() {
-		return hwnd;
-	}
-
-	/**
-	 * <p>
-	 * Returns the resource id of the window.
-	 * </p>
-	 * 
-	 * @return resource id of the window
-	 */
-	public int getResourceId() {
-		return resourceId;
-	}
-
-	/**
-	 * <p>
-	 * Returns the class name of the window.
-	 * </p>
-	 * 
-	 * @return class name of the window
-	 */
-	public String getClassName() {
-		return className;
-	}
-
-	/**
-	 * <p>
-	 * Sets the name of the window.
-	 * </p>
-	 * 
-	 * @param text
-	 *            new name of the window
-	 */
-	public void setName(String text) {
-		windowName = text;
-	}
-
-	/**
-	 * <p>
-	 * Removes a the window and all its children from the {@link WindowTree}.
-	 * </p>
-	 * 
-	 * @return list of the children of the window for further clean up.
-	 */
-	public List<WindowTreeNode> remove() {
-		if (parent != null) {
-			parent.removeChild(this);
-		}
-		return children;
-	}
-
-	/**
-	 * <p>
-	 * Removes a child window.
-	 * </p>
-	 * 
-	 * @param child
-	 *            reference to the child window to be removed
-	 */
-	public void removeChild(WindowTreeNode child) {
-		children.remove(child);
-	}
-
-	/**
-	 * <p>
-	 * Adds a new child window and creates WindowTreeNode for it.
-	 * </p>
-	 * 
-	 * @param childHwnd
-	 *            hwnd of the child window
-	 * @param childWindowName
-	 *            name of the child window
-	 * @param resourceId
-	 *            resource id of the child window
-	 * @param className
-	 *            class name of the child window
-	 * @param isModal
-	 *            modality of the child window
-	 * @return reference to the WindowTreeNode created for the child window
-	 */
-	public WindowTreeNode addChild(int childHwnd, String childWindowName,
-			int resourceId, String className, boolean isModal) {
-		WindowTreeNode child = new WindowTreeNode(childHwnd, this,
-				childWindowName, resourceId, className, isModal);
-		children.add(child);
-		return child;
-	}
-
-	/**
-	 * <p>
-	 * Returns a string identfier of the window:<br>
-	 * {@code [resourceId;"windowName";"className";modality]}
-	 * </p>
-	 * 
-	 * @return identifier string of the window
-	 */
-	@Override
-	public String toString() {
-		return "[" + resourceId + ";\"" + windowName + "\";\"" + className
-				+ "\";" + isModal + "]";
-	}
-
-	/**
-	 * <p>
-	 * Returns an XML representation of the window, including its parents. It is
-	 * defined as follows:<br>
-	 * <code>
-	 * parent#xmlRepresentation()<br>
-	 * &lt;window name="this.windowname" class="this.className" resourceId="this.resourceId" isModal="this.isModel"/&gt;
-	 * </code>
-	 * </p>
-	 * 
-	 * @return xml representation of the window
-	 */
-	public String xmlRepresentation() {
-		String xmlString = "";
-		if (parent != null) {
-			xmlString = parent.xmlRepresentation();
-		}
-		xmlString += "<window name=\""
-				+ StringTools.xmlEntityReplacement(windowName) + "\" class=\""
-				+ StringTools.xmlEntityReplacement(className)
-				+ "\" resourceId=\"" + resourceId + "\" isModal=\"" + isModal
-				+ "\" hwnd=\"" + hwnd + "\""
-				+ "/>";
-		return xmlString;
-	}
-
-	/**
-	 * <p>
-	 * Returns the names of the parents and itself separated by dots, e.g.,
-	 * "GrandParent.Parent.windowName"
-	 * </p>
-	 * 
-	 * @return names of the parents separated by dots
-	 */
-	public String getParentNames() {
-		String parentNames = "";
-		if (parent != null) {
-			parentNames = parent.getParentNames() + ".";
-		}
-		parentNames += windowName;
-		return parentNames;
-	}
+public class MFCGUIElementSpec implements IGUIElementSpec {
+
+    /**
+     * <p>
+     * current name of the window
+     * </p>
+     */
+    private String name;
+
+    /**
+     * <p>
+     * previous names of the window as it may have changed over time.
+     * </p>
+     */
+    private List<String> formerNames = new ArrayList<String>();
+
+    /**
+     * <p>
+     * Handle of the window. Used as unique identifier during its existence.
+     * </p>
+     */
+    private long hwnd;
+
+    /**
+     * <p>
+     * previous handles of the window as the window may have been destroyed and recreated
+     * </p>
+     */
+    private List<Long> formerHwnds = new ArrayList<Long>();
+
+    /**
+     * <p>
+     * Resource id of the window.
+     * </p>
+     */
+    private final int resourceId;
+
+    /**
+     * <p>
+     * type (class name) of the window.
+     * </p>
+     */
+    private final String type;
+
+    /**
+     * <p>
+     * True, if the window is modal.
+     * </p>
+     */
+    private final boolean isModal;
+
+    /**
+     * <p>
+     * Creates a new WindowTreeNode.
+     * </p>
+     * <p>
+     * The constructor is protected WindowTreeNode may only be created from the WindowTree.
+     * </p>
+     * 
+     * @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;
+    }
+
+    /**
+     * <p>
+     * Returns the name of the window.
+     * </p>
+     * 
+     * @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();
+    }
+
+    /**
+     * <p>
+     * Returns the hwnd of the window.
+     * </p>
+     * 
+     * @return hwnd of the window
+     */
+    public long getHwnd() {
+        return hwnd;
+    }
+
+    /**
+     * <p>
+     * Returns the resource id of the window.
+     * </p>
+     * 
+     * @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;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @return
+     */
+    public boolean isModal() {
+        return isModal;
+    }
+
+    /**
+     * <p>
+     * Sets the name of the window.
+     * </p>
+     * 
+     * @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;
+    }
+
+    /**
+     * <p>
+     * Sets the hwnd of the window.
+     * </p>
+     * 
+     * @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 to 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();
+    }
+
+    /**
+     * <p>
+     * Returns a string identfier of the window:<br>
+     * {@code [resourceId;"windowName";"className";modality]}
+     * </p>
+     * 
+     * @return identifier string of the window
+     */
+    @Override
+    public String toString() {
+        return "[" + resourceId + ";" + getName() + ";\"" + type + "\";" + isModal + ";" +
+            hwnd + "]";
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     */
+    String toXML() {
+        return
+            "<window name=\"" + (name != null ? StringTools.xmlEntityReplacement(name) : "") +
+            "\" class=\"" + StringTools.xmlEntityReplacement(type) +
+            "\" resourceId=\"" + resourceId + "\" isModal=\"" +
+            isModal + "\" hwnd=\"" + hwnd + "\"" + "/>";
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @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);
+            }
+        }
+    }
 
 }
