Index: trunk/autoquest-htmlmonitor/src/main/assembly/config.xml
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/assembly/config.xml	(revision 1069)
+++ trunk/autoquest-htmlmonitor/src/main/assembly/config.xml	(revision 1069)
@@ -0,0 +1,19 @@
+<assembly
+    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+  <id>config</id>
+  <formats>
+    <format>zip</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>data</directory>
+      <outputDirectory></outputDirectory>
+      <includes>
+        <include>**/*</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+</assembly>
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlDocument.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlDocument.java	(revision 1069)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlDocument.java	(revision 1069)
@@ -0,0 +1,144 @@
+//   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.htmlmonitor;
+
+/**
+ * <p>
+ * represents a document on a web server including its path and query in the URL.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+class HtmlDocument extends HtmlGUIElement {
+
+    /**
+     * the server on which the document resists
+     */
+    private HtmlServer server;
+    
+    /**
+     * the path to the document on the server
+     */
+    private String path;
+    
+    /**
+     * the query on the path that created the document (including the leading questionmark)
+     */
+    private String query;
+    
+    /**
+     * the title of the document, null if none is present
+     */
+    private String title;
+    
+    /**
+     * <p>
+     * instantiates a new document element
+     * </p>
+     *
+     * @param id     the id of the document
+     * @param server the server on which the document resists
+     * @param path   the path to the document on the server
+     * @param query  the query on the path that created the document (may include the questionmark)
+     * @param title  the title of the document, null if none is present
+     */
+    HtmlDocument(String id, HtmlServer server, String path, String query, String title) {
+        super(id, server);
+
+        if (server == null) {
+            throw new IllegalArgumentException("server must not be null");
+        }
+
+        if (path == null) {
+            throw new IllegalArgumentException("path must not be null");
+        }
+
+        this.server = server;
+        this.path = path;
+        this.query = query;
+        this.title = title;
+        
+        if ((this.query != null) && (!this.query.startsWith("?"))) {
+            this.query = "?" + this.query;
+        }
+    }
+
+    /**
+     * @return the server on which the document resists
+     */
+    HtmlServer getServer() {
+        return server;
+    }
+
+    /**
+     * @return the path to the document on the server
+     */
+    String getPath() {
+        return path;
+    }
+
+    /**
+     * @return the query on the path that created the document (including the leading questionmark)
+     */
+    String getQuery() {
+        return query;
+    }
+
+    /**
+     * @return the title of the document, null if none is present
+     */
+    String getTitle() {
+        return title;
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement#equals(de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement)
+     */
+    @Override
+    public boolean equals(HtmlGUIElement obj) {
+        if (this == obj) {
+            return true;
+        }
+        else if (obj instanceof HtmlDocument) {
+            return equals((HtmlDocument) obj);
+        }
+        else {
+            return false;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement#equals(de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement)
+     */
+    public boolean equals(HtmlDocument other) {
+        if (this == other) {
+            return true;
+        }
+
+        return (server.equals(other.server) && path.equals(other.path) &&
+                (query != null ? query.equals(other.query) : other.query == null) &&
+                (title != null ? title.equals(other.title) : other.title == null));
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return server.hashCode() + path.hashCode() + (query != null ? query.hashCode() : 0) +
+            (title != null ? title.hashCode() : 0);
+    }
+
+}
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlEvent.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlEvent.java	(revision 1023)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlEvent.java	(revision 1069)
@@ -38,7 +38,17 @@
 
     /**
-     * the path in the HTML DOM to the object on which the event was executed
-     */
-    private String path;
+     * the HTML element on which the event was executed
+     */
+    private HtmlPageElement target;
+
+    /**
+     * the document to which the HTML element on which the event was executed belongs
+     */
+    private HtmlDocument targetDocument;
+
+    /**
+     * the targets DOM path through the document to which it belongs
+     */
+    private String targetDOMPath;
 
     /**
@@ -69,10 +79,10 @@
     /**
      * <p>
-     * initializes the event with all relevantinfos
+     * initializes the event with all relevant infos
      * </p>
      *
      * @param clientInfos    infos about the client that caused the event
      * @param time           the time stamp of the event
-     * @param path           the path in the HTML DOM to the object on which the event was executed
+     * @param target         the HTML element on which the event was executed
      * @param eventType      the type of the event, e.g. onclick
      * @param coordinates    the coordinates of the event, usually an array with two values
@@ -86,5 +96,5 @@
     HtmlEvent(HtmlClientInfos clientInfos,
               Long            time,
-              String          path,
+              HtmlPageElement target,
               String          eventType,
               Integer[]       coordinates,
@@ -95,5 +105,7 @@
         this.clientInfos = clientInfos;
         this.time = time;
-        this.path = path;
+        this.target = target;
+        this.targetDocument = target.getDocument();
+        this.targetDOMPath = target.getDOMPath();
         this.eventType = eventType;
         this.coordinates = coordinates;
@@ -104,4 +116,46 @@
 
     /**
+     * <p>
+     * initializes the event for which the id of the target is not known yet. In this case
+     * the document and DOM path for the target are provided
+     * </p>
+     *
+     * @param clientInfos    infos about the client that caused the event
+     * @param time           the time stamp of the event
+     * @param targetDocument the document to which the HTML element belongs on which the event was
+     *                       executed
+     * @param targetDOMPath  the path through the DOM of the document of the HTML element on which
+     *                       the event was executed
+     * @param eventType      the type of the event, e.g. onclick
+     * @param coordinates    the coordinates of the event, usually an array with two values
+     *                       (x and y)
+     * @param key            if the event is a key event, the key that was pressed or released
+     * @param scrollPosition if the event is a scroll event, the resulting position of the
+     *                       scrolled element
+     * @param selectedValue  if the event is an on change event, the value to which the changed
+     *                       element is changed
+     */
+    HtmlEvent(HtmlClientInfos clientInfos,
+              Long            time,
+              HtmlDocument    targetDocument,
+              String          targetDOMPath,
+              String          eventType,
+              Integer[]       coordinates,
+              Integer         key,
+              Integer[]       scrollPosition,
+              String          selectedValue)
+    {
+        this.clientInfos = clientInfos;
+        this.time = time;
+        this.targetDocument = targetDocument;
+        this.targetDOMPath = targetDOMPath;
+        this.eventType = eventType;
+        this.coordinates = coordinates;
+        this.key = key;
+        this.scrollPosition = scrollPosition;
+        this.selectedValue = selectedValue;
+    }
+
+    /**
      * @return the clientInfos
      */
@@ -118,8 +172,22 @@
 
     /**
-     * @return the path
-     */
-    String getPath() {
-        return path;
+     * @return the target
+     */
+    HtmlPageElement getTarget() {
+        return target;
+    }
+
+    /**
+     * @return the targetDocument
+     */
+    HtmlDocument getTargetDocument() {
+        return targetDocument;
+    }
+
+    /**
+     * @return the targetDOMPath
+     */
+    String getTargetDOMPath() {
+        return targetDOMPath;
     }
 
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlGUIElement.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlGUIElement.java	(revision 1069)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlGUIElement.java	(revision 1069)
@@ -0,0 +1,134 @@
+//   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.htmlmonitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * represents an element of an HTML GUI. This can be a server, which is usually the root of a
+ * GUI structure, a web page, or a tag within a web page.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+abstract class HtmlGUIElement {
+
+    /**
+     * the children of this element
+     */
+    private List<HtmlGUIElement> children;
+    
+    /**
+     * the id of this GUI element
+     */
+    private String id;
+
+    /**
+     * the parent GUI element, null if none is present
+     */
+    private HtmlGUIElement parent;
+
+    /**
+     * <p>
+     * instantiates a new GUI element
+     * </p>
+     * 
+     * @param parentId the parent GUI element, null if none is present
+     */
+    protected HtmlGUIElement(String id, HtmlGUIElement parent) {
+        if (id == null) {
+            throw new IllegalArgumentException("id must not be null");
+        }
+        
+        this.id = id;
+        this.parent = parent;
+    }
+
+    /**
+     * <p>
+     * returns the children of this element if any, null else
+     * </p>
+     * 
+     * @return the children
+     */
+    List<HtmlGUIElement> getChildren() {
+        return children;
+    }
+
+
+    /**
+     * adds a child to this element. Merges children, for which the equal method returns true
+     * 
+     * @param child the child to be added
+     */
+    void addChild(HtmlGUIElement newChild) {
+        if (newChild != null) {
+            if (children == null) {
+                children = new ArrayList<HtmlGUIElement>();
+            }
+            
+            boolean added = false;
+            for (HtmlGUIElement child : children) {
+                if (child.equals(newChild)) {
+                    if (newChild.getChildren() != null) {
+                        for (HtmlGUIElement subchild : newChild.getChildren()) {
+                            child.addChild(subchild);
+                        }
+                    }
+                    
+                    added = true;
+                    break;
+                }
+            }
+            
+            if (!added) {
+                children.add(newChild);
+            }
+        }
+    }
+    
+    /**
+     * returns the id of the GUI element.
+     * 
+     * @return the id of the GUI element
+     */
+    String getId() {
+        return id;
+    }
+
+    /**
+     * <p>
+     * returns the id of the parent GUI element, if any
+     * </p>
+     *
+     * @return the id of the parent GUI element, or null, if this element does not have a parent
+     */
+    String getParentId() {
+        if (parent == null) {
+            return null;
+        }
+        else {
+            return parent.getId();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public abstract boolean equals(HtmlGUIElement obj);
+
+}
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlGUIElementManager.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlGUIElementManager.java	(revision 1069)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlGUIElementManager.java	(revision 1069)
@@ -0,0 +1,203 @@
+//   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.htmlmonitor;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+class HtmlGUIElementManager {
+
+    /** */
+    private Map<String, HtmlGUIElement> idMap = new HashMap<String, HtmlGUIElement>();
+    
+    /** */
+    private Map<String, List<HtmlPageElement>> domPathMap =
+          new HashMap<String, List<HtmlPageElement>>();
+    
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     */
+    HtmlServer createHtmlServer(String name, int port) {
+        if (name == null) {
+            throw new IllegalArgumentException("name must not be null");
+        }
+        
+        String id = calculateId(name, Integer.toString(port));
+        
+        HtmlGUIElement server = idMap.get(id);
+        
+        if (server == null) {
+            server = new HtmlServer(id, name, port);
+            idMap.put(id, server);
+        }
+        else if (!(server instanceof HtmlServer)) {
+            throw new RuntimeException
+                ("id conflict: calculated the same id for two different GUI elements");
+        }
+        
+        return (HtmlServer) server;
+    }
+    
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     */
+    HtmlDocument createHtmlDocument(HtmlServer server, String path, String query, String title) {
+        if (server == null) {
+            throw new IllegalArgumentException("server must not be null");
+        }
+        if (path == null) {
+            throw new IllegalArgumentException("path must not be null");
+        }
+
+        String id = calculateId(server.getId(), path, query, title);
+        
+        HtmlGUIElement document = idMap.get(id);
+
+        if (document == null) {
+            document = new HtmlDocument(id, server, path, query, title);
+            idMap.put(id, document);
+        }
+        else if (!(document instanceof HtmlDocument)) {
+            throw new RuntimeException
+                ("id conflict: calculated the same id for two different GUI elements");
+        }
+        
+        return (HtmlDocument) document;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param document
+     * @param parent
+     * @param tagName
+     * @param htmlid
+     * @param index
+     * @return
+     */
+    HtmlPageElement createHtmlPageElement(HtmlDocument    document,
+                                          HtmlPageElement parent,
+                                          String          tagName,
+                                          String          htmlId,
+                                          Integer         index)
+    {
+        if (document == null) {
+            throw new IllegalArgumentException("document must not be null");
+        }
+        if (tagName == null) {
+            throw new IllegalArgumentException("document must not be null");
+        }
+
+        String id = calculateId
+            (document.getId(), parent != null ? parent.getDOMPath() : null, tagName, htmlId,
+             index != null ? index.toString() : "-1");
+
+        HtmlGUIElement pageElement = idMap.get(id);
+
+        if (pageElement == null) {
+            pageElement = new HtmlPageElement(id, document, parent, tagName, htmlId, index);
+            idMap.put(id, pageElement);
+        }
+        else if (!(pageElement instanceof HtmlPageElement)) {
+            throw new RuntimeException
+                ("id conflict: calculated the same id for two different GUI elements");
+        }
+            
+        List<HtmlPageElement> candidates =
+            domPathMap.get(((HtmlPageElement) pageElement).getDOMPath());
+        
+        if (candidates == null) {
+            candidates = new LinkedList<HtmlPageElement>();
+            domPathMap.put(((HtmlPageElement) pageElement).getDOMPath(), candidates);
+        }
+        
+        if (!candidates.contains(pageElement)) {
+            candidates.add((HtmlPageElement) pageElement);
+        }
+
+        return (HtmlPageElement) pageElement;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param document
+     * @param domPath
+     * @return
+     */
+    HtmlPageElement getPageElement(HtmlDocument document, String domPath) {
+        List<HtmlPageElement> candidates = domPathMap.get(domPath);
+        
+        if (candidates != null) {
+            for (HtmlPageElement candidate : candidates) {
+                if (document.equals(candidate.getDocument())) {
+                    return candidate;
+                }
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param name
+     * @param string
+     * @return
+     */
+    private String calculateId(String... fragments) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-512");
+            
+            for (String fragment : fragments) {
+                if (fragment != null) {
+                    md.update(fragment.getBytes());
+                }
+            }
+            
+            return Base64.encodeBase64String(md.digest());
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("Java VM does not support this code");
+        }
+    }
+
+}
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorLogManager.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorLogManager.java	(revision 1023)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorLogManager.java	(revision 1069)
@@ -116,5 +116,5 @@
     @Override
     public void handleMessage(HtmlClientInfos clientInfos,
-                              HtmlPageElement guiStructure,
+                              HtmlGUIElement  guiStructure,
                               HtmlEvent[]     events)
     {
@@ -140,4 +140,5 @@
             Console.printerrln("could not handle message of client " + clientInfos.getClientId() +
                                ": " + e);
+            e.printStackTrace();
             Console.logException(e);
             
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorMessageListener.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorMessageListener.java	(revision 1023)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorMessageListener.java	(revision 1069)
@@ -36,5 +36,5 @@
      */
     void handleMessage(HtmlClientInfos clientInfos,
-                       HtmlPageElement guiStructure,
+                       HtmlGUIElement  guiStructure,
                        HtmlEvent[]     events);
 
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorOutputWriter.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorOutputWriter.java	(revision 1023)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorOutputWriter.java	(revision 1069)
@@ -179,5 +179,5 @@
     @Override
     public void handleMessage(HtmlClientInfos clientInfos,
-                              HtmlPageElement guiStructure,
+                              HtmlGUIElement  guiStructure,
                               HtmlEvent[]     events)
     {
@@ -214,19 +214,30 @@
      * @param guiStructure the GUI structure to be logged
      */
-    private void dumpGuiStructure(HtmlPageElement guiStructure) {
-        outputWriter.print("<component path=\"");
-        outputWriter.print(guiStructure.getPath());
+    private void dumpGuiStructure(HtmlGUIElement guiStructure) {
+        outputWriter.print("<component id=\"");
+        outputWriter.print(guiStructure.getId());
         outputWriter.println("\">");
         
-        dumpParam("class", guiStructure.getTagName());
-        dumpParam("htmlId", guiStructure.getId());
-        dumpParam("title", guiStructure.getTitle());
-        dumpParam("index", guiStructure.getIndex());
-        dumpParam("parent", guiStructure.getParentPath());
+        if (guiStructure instanceof HtmlServer) {
+            dumpParam("host", ((HtmlServer) guiStructure).getName());
+            dumpParam("port", ((HtmlServer) guiStructure).getPort());
+        }
+        else if (guiStructure instanceof HtmlDocument) {
+            dumpParam("path", ((HtmlDocument) guiStructure).getPath());
+            dumpParam("query", ((HtmlDocument) guiStructure).getQuery());
+            dumpParam("title", ((HtmlDocument) guiStructure).getTitle());
+        }
+        else if (guiStructure instanceof HtmlPageElement) {
+            dumpParam("tagname", ((HtmlPageElement) guiStructure).getTagName());
+            dumpParam("htmlid", ((HtmlPageElement) guiStructure).getHtmlId());
+            dumpParam("index", ((HtmlPageElement) guiStructure).getIndex());
+        }
+        
+        dumpParam("parent", guiStructure.getParentId());
         
         outputWriter.println("</component>");
         
         if (guiStructure.getChildren() != null) {
-            for (HtmlPageElement child : guiStructure.getChildren()) {
+            for (HtmlGUIElement child : guiStructure.getChildren()) {
                 dumpGuiStructure(child);
             }
@@ -260,5 +271,12 @@
 
         dumpParam("selectedValue", event.getSelectedValue());
-        dumpParam("target", event.getPath());
+        
+        if (event.getTarget() != null) {
+            dumpParam("target", event.getTarget().getId());
+        }
+        else {
+            dumpParam("targetDocument", event.getTargetDocument().getId());
+            dumpParam("targetDOMPath", event.getTargetDOMPath());
+        }
         dumpParam("timestamp", event.getTime());
         
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServlet.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServlet.java	(revision 1023)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServlet.java	(revision 1069)
@@ -78,4 +78,9 @@
      * the message listener to forward received messages to.
      */
+    private HtmlGUIElementManager guiElementManager = new HtmlGUIElementManager();
+
+    /**
+     * the message listener to forward received messages to.
+     */
     private transient HtmlMonitorMessageListener messageListener;
 
@@ -199,5 +204,5 @@
             }
             else {
-                HtmlPageElement guiStructure = extractHtmlPageElements(message, clientInfos);
+                HtmlGUIElement guiStructure = extractHtmlPageElements(message, clientInfos);
                 HtmlEvent[] events = extractHtmlEvents(message, clientInfos);
                 
@@ -275,6 +280,6 @@
             events = new ArrayList<HtmlEvent>();
             
-            HtmlPageElement server = getServerElement(clientInfos);
-            HtmlPageElement page = getPageElementRepresentingWebPage(clientInfos, server);
+            HtmlServer server = getServerElement(clientInfos);
+            HtmlDocument document = getPageElementRepresentingWebPage(clientInfos, server);
 
             for (int i = 0; i < eventArray.size(); i++) {
@@ -285,5 +290,5 @@
                 else {
                     Long time = assertValue(((JSONObject) eventObj), "time", Long.class);
-                    String path = assertValue(((JSONObject) eventObj), "path", String.class);
+                    String domPath = assertValue(((JSONObject) eventObj), "path", String.class);
                     String eventType =
                         assertValue(((JSONObject) eventObj), "eventType", String.class);
@@ -302,6 +307,6 @@
                         Console.printerrln(eventType + " event has no valid timestamp");
                     }
-                    else if (path == null) {
-                        Console.printerrln(eventType + " event has no valid path");
+                    else if (domPath == null) {
+                        Console.printerrln(eventType + " event has no valid DOM path");
                     }
                     else if ((coordinates != null) && (coordinates.length != 2)) {
@@ -311,7 +316,17 @@
                                 (eventType, coordinates, key, scrollPosition, selectedValue))
                     {
-                        path = page.getPath() + path;
-                        events.add(new HtmlEvent(clientInfos, time, path, eventType,
-                                                 coordinates, key, scrollPosition, selectedValue));
+                        HtmlPageElement target =
+                            guiElementManager.getPageElement(document, domPath);
+                        
+                        if (target != null) {
+                            events.add(new HtmlEvent(clientInfos, time, target, eventType,
+                                                     coordinates, key, scrollPosition,
+                                                     selectedValue));
+                        }
+                        else {
+                            events.add(new HtmlEvent(clientInfos, time, document, domPath,
+                                                     eventType, coordinates, key, scrollPosition,
+                                                     selectedValue));
+                        }
                     }
                     else {
@@ -342,12 +357,12 @@
      *         representation of the server of the HTML page that was observed 
      */
-    private HtmlPageElement extractHtmlPageElements(JSONObject      object,
-                                                    HtmlClientInfos clientInfos)
+    private HtmlServer extractHtmlPageElements(JSONObject      object,
+                                               HtmlClientInfos clientInfos)
     {
-        HtmlPageElement server = getServerElement(clientInfos);
-        HtmlPageElement page = getPageElementRepresentingWebPage(clientInfos, server);
+        HtmlServer server = getServerElement(clientInfos);
+        HtmlDocument document = getPageElementRepresentingWebPage(clientInfos, server);
 
         JSONObject jsonPageElement = assertValue(object, "guiModel", JSONObject.class);
-        page.addChild(convert(jsonPageElement, page.getPath()));
+        document.addChild(convert(jsonPageElement, document, null));
         
         return server;
@@ -364,11 +379,13 @@
      * @return as described
      */
-    private HtmlPageElement getServerElement(HtmlClientInfos clientInfos) {
-        String id = clientInfos.getUrl().getHost();
+    private HtmlServer getServerElement(HtmlClientInfos clientInfos) {
+        String host = clientInfos.getUrl().getHost();
+        int port = 80;
+        
         if (clientInfos.getUrl().getPort() > -1) {
-            id += ":" + clientInfos.getUrl().getPort();
-        }
-        
-        return new HtmlPageElement(null, "server", id, 0);
+            port = clientInfos.getUrl().getPort();
+        }
+        
+        return guiElementManager.createHtmlServer(host, port);
     }
 
@@ -384,19 +401,20 @@
      * @return as described
      */
-    private HtmlPageElement getPageElementRepresentingWebPage(HtmlClientInfos clientInfos,
-                                                              HtmlPageElement server)
+    private HtmlDocument getPageElementRepresentingWebPage(HtmlClientInfos clientInfos,
+                                                           HtmlServer      server)
     {
-        String id = clientInfos.getUrl().getPath();
+        String path = clientInfos.getUrl().getPath();
+        String query = null;
         
         if (clientInfos.getUrl().getQuery() != null) {
-            id += "?" + clientInfos.getUrl().getQuery();
-        }
-        
-        HtmlPageElement page =
-            new HtmlPageElement(server.getPath(), "document", id, clientInfos.getTitle(), 0);
-        
-        server.addChild(page);
-        
-        return page;
+            query = "?" + clientInfos.getUrl().getQuery();
+        }
+        
+        HtmlDocument document = guiElementManager.createHtmlDocument
+            (server, path, query, clientInfos.getTitle());
+        
+        server.addChild(document);
+        
+        return document;
     }
 
@@ -408,18 +426,24 @@
      *
      * @param jsonPageElement the JSON object to be converted
-     * @param parentPath      the path through the DOM of the parent HTML page element of the
-     *                        converted one
+     * @param document        the document to which the page element belongs
+     * @param parent          the parent page element of the converted element, of null, if none
+     *                        is present. In this case the document is considered the parent
+     *                        element.
      *                        
      * @return as described.
      */
-    private HtmlPageElement convert(JSONObject jsonPageElement, String parentPath) {
+    private HtmlPageElement convert(JSONObject      jsonPageElement,
+                                    HtmlDocument    document,
+                                    HtmlPageElement parent)
+    {
         HtmlPageElement result = null;
 
         if (jsonPageElement != null) {
             String tagName = assertValue(jsonPageElement, "tagName", String.class);
-            String id = assertValue(jsonPageElement, "id", String.class);
+            String htmlid = assertValue(jsonPageElement, "htmlId", String.class);
             Integer index = assertValue(jsonPageElement, "index", Integer.class);
 
-            result = new HtmlPageElement(parentPath, tagName, id, index);
+            result = guiElementManager.createHtmlPageElement
+                (document, parent, tagName, htmlid, index);
 
             JSONArray childElements = assertValue(jsonPageElement, "children", JSONArray.class);
@@ -435,5 +459,5 @@
                     }
                     else {
-                        result.addChild(convert((JSONObject) jsonChild, result.getPath()));
+                        result.addChild(convert((JSONObject) jsonChild, document, result));
                     }
                 }
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlPageElement.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlPageElement.java	(revision 1023)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlPageElement.java	(revision 1069)
@@ -15,38 +15,32 @@
 package de.ugoe.cs.autoquest.htmlmonitor;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * <p>
- * represents an element of an HTML GUI. This can be a server, which is usually the root of a
- * GUI structure, a web page, or a tag within a web page.
+ * represents an element of an HTML GUI, i.e. a tag within a web page.
  * </p>
  * 
  * @author Patrick Harms
  */
-class HtmlPageElement {
-
-    /**
-     * path to the parent of this page element or null, if this is the root element
-     */
-    private String parentPath;
-    
-    /**
-     * the name of the tag represented by this page element. May also be the name of the server
-     * or the page.
+class HtmlPageElement extends HtmlGUIElement {
+
+    /**
+     * the document to which the represented tag belongs
+     */
+    private HtmlDocument document;
+    
+    /**
+     * the parent page element; if null, the document is considered the parent
+     */
+    private HtmlPageElement parent;
+    
+    /**
+     * the name of the tag represented by this page element.
      */
     private String tagName;
     
     /**
-     * the id of the page element. May also be the name of the server including port number or the
-     * URL of the page
-     */
-    private String id;
-    
-    /**
-     * the title of the page if a page is represented through this element
-     */
-    private String title;
+     * the id of the page element inside the DOM.
+     */
+    private String htmlId;
     
     /**
@@ -57,23 +51,41 @@
     
     /**
-     * the children of this element
-     */
-    private List<HtmlPageElement> children;
-
-    /**
      * <p>
      * instantiates a new element representing a tag in an HTML page
      * </p>
      *
-     * @param parentPath the path through the DOM to the parent node of the represented tag 
-     * @param tagName    the name of the represented tag
-     * @param id         the id of the tag in the DOM
-     * @param index      the index of the represented tag regarding all tags with the same tag name
-     *                   in the list of children of the parent tag
-     */
-    HtmlPageElement(String parentPath, String tagName, String id, Integer index) {
-        this.parentPath = parentPath;
+     * @param id       the id of the page element
+     * @param document the document to which the represented tag belongs
+     * @param parent   the parent page element; if null, the document is considered the parent
+     * @param tagName  the name of the represented tag
+     * @param htmlId   the id of the tag in the DOM
+     * @param index    the index of the represented tag regarding all tags with the same
+     *                 tag name in the list of children of the parent tag
+     */
+    HtmlPageElement(String          id,
+                    HtmlDocument    document,
+                    HtmlPageElement parent,
+                    String          tagName,
+                    String          htmlId,
+                    Integer         index)
+    {
+        super(id, parent == null ? document : parent);
+        
+        if (document == null) {
+            throw new IllegalArgumentException("document must not be null");
+        }
+
+        if (tagName == null) {
+            throw new IllegalArgumentException("tagName must not be null");
+        }
+        
+        if ((htmlId == null) && (index == null)) {
+            throw new IllegalArgumentException("either one of htmlId and index must not be null");
+        }
+        
+        this.document = document;
+        this.parent = parent;
         this.tagName = tagName;
-        this.id = id;
+        this.htmlId = htmlId;
         this.index = index;
     }
@@ -81,23 +93,16 @@
     /**
      * <p>
-     * instantiates a new element representing an HTML page
+     * returns the document to which the represented tag belongs
      * </p>
      *
-     * @param parentPath the path through the DOM to the parent node of the represented tag which is
-     *                   usually a server
-     * @param tagName    the name of the represented tag which is the path of the web page
-     * @param title      the title of the web page
-     * @param id         the id of the web page which is its path
-     * @param index      usually 0
-     */
-    HtmlPageElement(String parentPath, String tagName, String id, String title, Integer index) {
-        this(parentPath, tagName, id, index);
-        this.title = title;
-    }
-
-    /**
-     * <p>
-     * returns the name of the tag represented by this page element. May also be the name of the
-     * server or the page.
+     * @return the document
+     */
+    HtmlDocument getDocument() {
+        return document;
+    }
+
+    /**
+     * <p>
+     * returns the name of the tag represented by this page element.
      * </p>
      * 
@@ -110,23 +115,11 @@
     /**
      * <p>
-     * returns the id of the page element. May also be the name of the server including port
-     * number or the URL of the page
+     * returns the id of the page element.
      * </p>
      * 
      * @return the id
      */
-    String getId() {
-        return id;
-    }
-
-    /**
-     * <p>
-     * returns the title of the page if a page is represented through this element
-     * </p>
-     * 
-     * @return the title
-     */
-    String getTitle() {
-        return title;
+    String getHtmlId() {
+        return htmlId;
     }
 
@@ -141,42 +134,4 @@
     Integer getIndex() {
         return index;
-    }
-
-    /**
-     * <p>
-     * returns the children of this element if any, null else
-     * </p>
-     * 
-     * @return the children
-     */
-    List<HtmlPageElement> getChildren() {
-        return children;
-    }
-
-
-    /**
-     * adds a child to this element
-     * 
-     * @param child the child to be added
-     */
-    void addChild(HtmlPageElement child) {
-        if (child != null) {
-            if (children == null) {
-                children = new ArrayList<HtmlPageElement>();
-            }
-            
-            children.add(child);
-        }
-    }
-
-    /**
-     * <p>
-     * returns the path to the parent of this page element or null, if this is the root element
-     * </p>
-     *
-     * @return as described
-     */
-    String getParentPath() {
-        return parentPath;
     }
 
@@ -190,8 +145,8 @@
      * @return as described
      */
-    String getPath() {
+    String getDOMPath() {
         StringBuffer result = new StringBuffer();
-        if (parentPath != null) {
-            result.append(parentPath);
+        if (parent != null) {
+            result.append(parent.getDOMPath());
         }
 
@@ -199,7 +154,7 @@
         result.append(tagName);
         
-        if ((id != null) && (!"".equals(id))) {
-            result.append("(id=");
-            result.append(id);
+        if ((htmlId != null) && (!"".equals(htmlId))) {
+            result.append("(htmlId=");
+            result.append(htmlId);
             result.append(")");
         }
@@ -213,3 +168,42 @@
     }
 
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement#equals(de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement)
+     */
+    @Override
+    public boolean equals(HtmlGUIElement obj) {
+        if (this == obj) {
+            return true;
+        }
+        else if (obj instanceof HtmlPageElement) {
+            return equals((HtmlPageElement) obj);
+        }
+        else {
+            return false;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement#equals(de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement)
+     */
+    public boolean equals(HtmlPageElement other) {
+        if (this == other) {
+            return true;
+        }
+
+        return (document.equals(other.document) && tagName.equals(other.tagName) &&
+                (parent != null ? parent.equals(other.parent) : other.parent == null) &&
+                (htmlId != null ? htmlId.equals(other.htmlId) : other.htmlId == null) &&
+                (index != null ? index.equals(other.index) : other.index == null));
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return document.hashCode() + tagName.hashCode() + (parent != null ? parent.hashCode() : 0) +
+            (htmlId != null ? htmlId.hashCode() : 0) + (index != null ? index : 0);
+    }
+
 }
Index: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlServer.java
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlServer.java	(revision 1069)
+++ trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlServer.java	(revision 1069)
@@ -0,0 +1,118 @@
+//   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.htmlmonitor;
+
+/**
+ * <p>
+ * represents a web server including a port.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+class HtmlServer extends HtmlGUIElement {
+
+    /**
+     * the name of the represented server
+     */
+    private String name;
+    
+    /**
+     * the port of the server (default is 80)
+     */
+    private int port = 80;
+    
+    /**
+     * <p>
+     * instantiates a new server element
+     * </p>
+     *
+     * @param id   the id of the server
+     * @param name the name of the server
+     * @param port the port on the server
+     *             (must be between 0 and 65536, otherwise default 80 is used)
+     */
+    HtmlServer(String id, String name, int port) {
+        super(id, null);
+        
+        if (name == null) {
+            throw new IllegalArgumentException("name must not be null");
+        }
+
+        this.name = name;
+        
+        if ((0 < port) && (port < 65536)) {
+            this.port = port;
+        }
+
+    }
+
+    /**
+     * <p>
+     * returns the name of the server
+     * </p>
+     * 
+     * @return the name
+     */
+    String getName() {
+        return name;
+    }
+
+    /**
+     * <p>
+     * returns the port on the server
+     * </p>
+     * 
+     * @return the port
+     */
+    int getPort() {
+        return port;
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement#equals(de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement)
+     */
+    @Override
+    public boolean equals(HtmlGUIElement obj) {
+        if (this == obj) {
+            return true;
+        }
+        else if (obj instanceof HtmlServer) {
+            return equals((HtmlServer) obj);
+        }
+        else {
+            return false;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement#equals(de.ugoe.cs.autoquest.htmlmonitor.HtmlGUIElement)
+     */
+    public boolean equals(HtmlServer other) {
+        if (this == other) {
+            return true;
+        }
+
+        return (name.equals(other.name) && (port == other.port));
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return name.hashCode() + port;
+    }
+
+}
Index: trunk/autoquest-htmlmonitor/src/main/js/autoquest-htmlmonitor.js
===================================================================
--- trunk/autoquest-htmlmonitor/src/main/js/autoquest-htmlmonitor.js	(revision 1023)
+++ trunk/autoquest-htmlmonitor/src/main/js/autoquest-htmlmonitor.js	(revision 1069)
@@ -305,5 +305,5 @@
         
         if ((node.id) && (node.id !== "")) {
-            jsonRepresentation += "\"id\":\"" + node.id + "\"";
+            jsonRepresentation += "\"htmlId\":\"" + node.id + "\"";
         }
         else {
