Index: /trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCSimplifiedLogParser.java
===================================================================
--- /trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCSimplifiedLogParser.java	(revision 963)
+++ /trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCSimplifiedLogParser.java	(revision 963)
@@ -0,0 +1,686 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+import de.ugoe.cs.autoquest.plugin.jfc.eventcore.JFCEventId;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCComponentTree;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElementSpec;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * This class provides functionality to parse XML log files generated by the JFCMonitor of
+ * autoquest. The result of parsing a file is a collection of event sequences.
+ * </p>
+ * 
+ * @author Fabian Glaser
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class JFCSimplifiedLogParser extends DefaultHandler {
+
+    /**
+     * <p>
+     * Collection of event sequences that is contained in the log file, which is parsed.
+     * </p>
+     */
+    private Collection<List<Event>> sequences;
+
+    /**
+     * <p>
+     * Internal handle to the id of the event that is currently being parsed.
+     * </p>
+     */
+    private JFCEventId currentEventId;
+    
+    /**
+     * 
+     * <p>
+     * Internal handle to the hashcode of the component, that is currently parsed.
+     * </p>
+     */
+    private Long currentComponentHash;
+    
+    /**
+     * 
+     * <p>
+     * Internal handle to the hashcode of the parent of the component, that is currently parsed. 
+     * </p>
+     */
+    private Long currentParentHash;
+    
+    /**
+     * <p>
+     * Internal handle to the parameters of the event that is currently being parsed.
+     * </p>
+     */
+    private Long currentEventSource;
+     
+    /**
+     * <p>
+     * Internal handle to the parameters of the event currently being parsed.
+     * </p>
+     */
+    private Map<String, String> currentEventParameters;
+
+    /**
+     * <p>
+     * Internal handle to the event sequence that is currently being parsed.
+     * </p>
+     */
+    private List<Event> currentSequence;
+   
+    /**
+     * <p>
+     * internal handle to the parameters currently parsed for a component
+     * </p>
+     */
+    private JFCGUIElementSpec currentGuiElementSpec;
+
+    /**
+     * <p>
+     * internal handle to the parsed gui structure, stored in a JFCComponentTree
+     * </p>
+     */
+    private JFCComponentTree currentComponentTree;
+
+    /**
+     * <p>
+     * internal handle to the component of the previous event to be potentially reused for the
+     * current
+     * </p>
+     */
+    private IGUIElement lastGUIElement;
+    
+    /**
+     * <p>
+     * internal handle to the class ancestors
+     * </p> 
+     */
+    private List<String> currentAncestorList;
+
+    /**
+     * <p>
+     * this is used to check, if for every pressed key, there is a release of it
+     * </p>
+     */
+    private List<VirtualKey> mPressedKeys = new ArrayList<VirtualKey>();
+    
+    /**
+     * <p>
+     * a specification for event ids to be omitted by the parser
+     * </p>
+     */
+    private Collection<JFCEventId> eventFilter;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCLogParser with a default event filter. This ignores focus
+     * events, mouse pressed, and mouse released events.
+     * </p>
+     */
+    public JFCSimplifiedLogParser() {
+        sequences = new LinkedList<List<Event>>();
+        currentSequence = null;
+        //setupDefaultEventFilter();
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCLogParser with a specific event filter. The events in the
+     * provided collection are ignored by the parser. As events, the constants of the different
+     * event classes must be used. E.g. creating a collection and putting
+     * <code>MouseEvent.MOUSE_PRESSED</code> will cause the parser to ignore all mouse pressed
+     * events. If the provided collection is null, no event is ignored.
+     * </p>
+     * 
+     * @param ignoredEvents
+     *            the events to be ignored by the parser, can be null
+     */
+    public JFCSimplifiedLogParser(Collection<JFCEventId> ignoredEvents) {
+        sequences = new LinkedList<List<Event>>();
+        currentSequence = null;
+        eventFilter = ignoredEvents;
+    }
+
+    /**
+     * <p>
+     * Parses a log file written by the JFCMonitor and creates a collection of event sequences.
+     * </p>
+     * 
+     * @param filename
+     *            name and path of the log file
+     */
+    public void parseFile(String filename) {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename must not be null");
+        }
+
+        parseFile(new File(filename));
+    }
+
+    /**
+     * <p>
+     * Parses a log file written by the JFCMonitor and creates a collection of event sequences.
+     * </p>
+     * 
+     * @param file
+     *            name and path of the log file
+     */
+    public void parseFile(File file) {
+        if (file == null) {
+            throw new IllegalArgumentException("file must not be null");
+        }
+
+        SAXParserFactory spf = SAXParserFactory.newInstance();
+        spf.setValidating(true);
+
+        SAXParser saxParser = null;
+        InputSource inputSource = null;
+        try {
+            saxParser = spf.newSAXParser();
+            inputSource =
+                new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8"));
+        }
+        catch (UnsupportedEncodingException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        catch (ParserConfigurationException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        catch (SAXException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        catch (FileNotFoundException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        if (inputSource != null) {
+            inputSource.setSystemId("file://" + file.getAbsolutePath());
+            try {
+                if (saxParser == null) {
+                    throw new RuntimeException("SAXParser creation failed");
+                }
+                saxParser.parse(inputSource, this);
+            }
+            catch (SAXParseException e) {
+                Console.printerrln("Failure parsing file in line " + e.getLineNumber() +
+                    ", column " + e.getColumnNumber() + ".");
+                Console.logException(e);
+                return;
+            }
+            catch (SAXException e) {
+                Console.printerr("Error parsing file + " + file.getName());
+                Console.logException(e);
+                return;
+            }
+            catch (IOException e) {
+                Console.printerr("Error parsing file + " + file.getName());
+                Console.logException(e);
+                return;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the collection of event sequences that is obtained from parsing log files.
+     * </p>
+     * 
+     * @return collection of event sequences
+     */
+    public Collection<List<Event>> getSequences() {
+        return sequences;
+    }
+
+    /**
+     * <p>
+     * Returns the gui model that is obtained from parsing log files.
+     * </p>
+     * 
+     * @return GUIModel
+     */
+    public GUIModel getGuiModel() {
+        return currentComponentTree.getGUIModel();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
+     * java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String uri, String localName, String qName, Attributes atts)
+        throws SAXException{
+        if (qName.equals("sessions")) {
+            currentSequence = new LinkedList<Event>();
+            if (currentComponentTree == null)
+            	currentComponentTree = new JFCComponentTree();
+        }
+        if (qName.equals("newsession")) {
+            Console.traceln(Level.FINE, "start of session");
+            if (currentSequence != null && !currentSequence.isEmpty()) {
+                // create a copy of the list just to have a correctly typed one.
+                sequences.add(currentSequence.subList(0, currentSequence.size() - 1));
+            }
+            currentSequence = new LinkedList<Event>();
+        }
+        else if (qName.equals("component")) {
+        	currentComponentHash = Long.parseLong(atts.getValue("hash"), 16);
+        	currentGuiElementSpec = new JFCGUIElementSpec();
+        	currentGuiElementSpec.setElementHash((int) currentComponentHash.longValue());
+        }
+        else if (qName.equals("event")) {
+            JFCEventId eventId = JFCEventId.parseEventId(atts.getValue("id"));
+            if ((eventFilter == null) || (!eventFilter.contains(eventId))) {
+                currentEventId = eventId;
+                currentEventParameters = new HashMap<String, String>();
+            }
+        }
+        else if (qName.equals("componentNameChange")) {
+        	Long sourceHash = Long.parseLong(atts.getValue("hash"), 16);
+        	String newName = atts.getValue("newName");
+        	int titleSource = Integer.parseInt(atts.getValue("titleSource"));
+        	JFCGUIElement sourceElement = (JFCGUIElement) currentComponentTree.find(sourceHash);
+        	JFCGUIElementSpec sourceSpec = (JFCGUIElementSpec) sourceElement.getSpecification();
+        	sourceSpec.setName(newName);
+        }
+        else if (qName.equals("param")) {
+        	if (currentEventId != null){
+        		if ("source".equals(atts.getValue("name"))){
+        			currentEventSource = Long.parseLong(atts.getValue("value"), 16);
+        		}
+                currentEventParameters.put(atts.getValue("name"), atts.getValue("value"));
+            } else if(currentComponentHash != null){
+            	if ("title".equals(atts.getValue("name"))) {
+        			currentGuiElementSpec.setName(atts.getValue("value"));
+        		}
+        		else if ("class".equals(atts.getValue("name"))) {
+        			currentGuiElementSpec.setType(atts.getValue("value"));
+        		}
+        		else if ("icon".equals(atts.getValue("name"))) {
+        			currentGuiElementSpec.setIcon(atts.getValue("value"));
+        		}
+        		else if ("index".equals(atts.getValue("name"))) {
+        			currentGuiElementSpec.setIndex(Integer.parseInt(atts.getValue("value")));
+        		}
+        		else if ("parent".equals(atts.getValue("name"))) {
+        			currentParentHash = Long.parseLong(atts.getValue("value"), 16);
+        		}
+            }
+        }
+        else if (qName.equals("ancestor")){
+        		currentAncestorList.add(atts.getValue("name"));
+        }
+        else if (qName.equals("ancestors")){
+        		currentAncestorList = new LinkedList<String>();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
+     * java.lang.String)
+     */
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (qName.equals("sessions")) {
+            if (currentSequence != null && !currentSequence.isEmpty()) {
+                sequences.add(currentSequence);
+            }
+            currentSequence = null;
+        }
+        else if (qName.equals("component") && currentComponentHash != null) {
+        	currentComponentTree.add(currentParentHash, currentComponentHash, currentGuiElementSpec);
+        	// do something with the ancestor list here
+        	
+        	currentComponentHash = null;
+        	currentParentHash = null;
+        	currentAncestorList = null;
+        }
+        else if (currentEventId != null) {
+            if (qName.equals("event")) {
+                
+                IGUIElement currentGUIElement;
+                currentGUIElement = currentComponentTree.find(currentEventSource);
+                
+                Event event = new Event
+                    (instantiateInteraction(currentEventId, currentEventParameters),
+                     (currentGUIElement == null ? lastGUIElement : currentGUIElement));
+                
+                currentSequence.add(event);
+                
+                currentEventParameters = null;
+                currentEventId = null;
+                
+                if (currentGUIElement != null) {
+                    lastGUIElement = currentGUIElement;
+                }
+                
+                currentGUIElement = null;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * depending on the event id and the event parameters, this method instantiates the concrete
+     * interaction, that took place, i.e. the event type
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id is unknown
+     */
+    private IInteraction instantiateInteraction(JFCEventId          eventId,
+                                                Map<String, String> eventParameters)
+      throws SAXException
+    {
+        switch (eventId)
+        {
+            case FOCUS_GAINED:
+                return handleNewFocus(eventId, eventParameters);
+
+            case KEY_PRESSED:
+            case KEY_RELEASED:
+            case KEY_TYPED:
+                return handleKeyAction(eventId, eventParameters);
+
+            case MOUSE_CLICKED:
+            case MOUSE_PRESSED:
+            case MOUSE_RELEASED:
+            case MOUSE_MOVED:
+            case MOUSE_ENTERED:
+            case MOUSE_EXITED:
+            case MOUSE_DRAGGED:
+            case MOUSE_WHEEL:
+                return handleMouseAction(eventId, eventParameters);
+
+            default:
+                throw new SAXException("unhandled event id " + eventId);
+        }
+    }
+
+    /**
+     * <p>
+     * handles a mouse interaction. The method determines based on the event id and the parameters
+     * which mouse button is pressed, released or clicked.
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id or button index is unknown
+     */
+    private IInteraction handleMouseAction(JFCEventId eventId, Map<String, String> eventParameters)
+        throws SAXException
+    {
+        MouseButtonInteraction.Button button = null;
+
+        if (eventParameters.get("Button") != null)
+        {
+            int buttonId = Integer.parseInt(eventParameters.get("Button"));
+            if (buttonId == MouseEvent.BUTTON1)
+            {
+                button = MouseButtonInteraction.Button.LEFT;
+            }
+            else if (buttonId == MouseEvent.BUTTON2)
+            {
+                button = MouseButtonInteraction.Button.MIDDLE;
+            }
+            else if (buttonId == MouseEvent.BUTTON3)
+            {
+                button = MouseButtonInteraction.Button.RIGHT;
+            }
+            else
+            {
+                throw new SAXException("unknown mouse button index " + buttonId);
+            }
+        }
+
+        if (JFCEventId.MOUSE_CLICKED == eventId)
+        {
+            int x = Integer.parseInt(eventParameters.get("X"));
+            int y = Integer.parseInt(eventParameters.get("Y"));
+            return new MouseClick(button, x, y);
+        }
+        else if (JFCEventId.MOUSE_PRESSED == eventId)
+        {
+            int x = Integer.parseInt(eventParameters.get("X"));
+            int y = Integer.parseInt(eventParameters.get("Y"));
+            return new MouseButtonDown(button, x, y);
+        }
+        else if (JFCEventId.MOUSE_RELEASED == eventId)
+        {
+            int x = Integer.parseInt(eventParameters.get("X"));
+            int y = Integer.parseInt(eventParameters.get("Y"));
+            return new MouseButtonUp(button, x, y);
+        }
+        else
+        {
+            throw new SAXException("unknown event id " + eventId);
+        }
+    }
+
+    /**
+     * <p>
+     * handles a keyboard interaction. The method determines based on the event id and the
+     * parameters which key on the keyboard is pressed or released. It further checks, if for 
+     * every released key there is also a pressed event
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id is unknown or if there is a key
+     *                      release without a preceding press of the same key
+     */
+    private IInteraction handleKeyAction(JFCEventId eventId, Map<String, String> eventParameters)
+      throws SAXException
+    {
+        // TODO handle shortcuts
+        if (JFCEventId.KEY_PRESSED == eventId)
+        {
+            VirtualKey key = VirtualKey.parseVirtualKey(eventParameters.get("KeyCode"));
+            mPressedKeys.add(key);
+
+            return new KeyPressed(key);
+        }
+        else if (JFCEventId.KEY_RELEASED == eventId)
+        {
+            VirtualKey key = VirtualKey.parseVirtualKey(eventParameters.get("KeyCode"));
+            if (mPressedKeys.contains(key))
+            {
+                mPressedKeys.remove(key);
+            }
+            else
+            {
+                Console.traceln(Level.SEVERE, "log file has an error, as it contains a key up event on key " +
+                                   key + " for which there is no preceeding key down event");
+            }
+
+            return new KeyReleased(key);
+        }
+
+        throw new SAXException("unknown event id " + eventId);
+    }
+
+    /**
+     * <p>
+     * handles explicit keyboard focus changes.
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id is unknown
+     */
+    private IInteraction handleNewFocus(JFCEventId eventId, Map<String, String> eventParameters)
+        throws SAXException
+    {
+        if (JFCEventId.FOCUS_GAINED == eventId)
+        {
+            return new KeyboardFocusChange();
+        }
+        else
+        {
+            throw new SAXException("unknown event id " + eventId);
+        }
+    }
+
+    /**
+     * <p>
+     * for some events in the log file, no component specification is provided. In this case the
+     * GUI element on which the event is executed must be determined based on the
+     * <code>toString</code> parameter of the event. This is achieved through this method. The
+     * <code>toString</code> parameter does not always carry sufficient information for the GUI
+     * elements. For example the title is not necessarily provided. Therefore some of this
+     * information is generated.
+     * </p>
+     *
+     * @param toStringValue
+     *            the <code>toString</code> parameter of the event to be parsed for the GUI element
+     *            
+     * @return the appropriate GUI Element
+     * 
+     * @throws SAXException thrown if the provided value of the <code>toString</code> parameter
+     *                      can not be parsed
+     */
+    private void getGUIElementSpecFromToString(String toStringValue)
+        throws SAXException
+    {
+        try
+        {
+            // match the following: <type>[<parameters>]
+            String pattern = "([\\w$\\.]*)\\[(.*)\\]";
+            Matcher matcher = Pattern.compile(pattern).matcher(toStringValue);
+
+            if (!matcher.find())
+            {
+                throw new IllegalArgumentException
+                    ("could not parse target from toString parameter");
+            }
+
+            String type = matcher.group(1);
+            
+            // match the following: <parameter>|,
+            // where <parameter> := <digitValue>|<value>|<key>"="<value>
+            pattern = "([\\w$@=\\.]*)|,";
+
+            matcher = Pattern.compile(pattern).matcher(matcher.group(2));
+            
+            float elementHash = -1;
+            
+            pattern = "(([\\d]*)|([\\w$]*)|(([\\w$@\\.]*)=([\\w$@\\.]*)))\\z";
+            Pattern valuePattern = Pattern.compile(pattern);
+            
+            while (matcher.find()) {
+                Matcher valueMatcher = valuePattern.matcher(matcher.group(1));
+                if (valueMatcher.find()) {
+                    if ((valueMatcher.group(2) != null) && (!"".equals(valueMatcher.group(2)))) {
+                        // found digit value. Those in combination usually denote the position
+                        // of the GUI element. So calculate an element has out of them
+                        elementHash += Integer.parseInt(valueMatcher.group(2));
+                    }
+                    else if ((valueMatcher.group(5) != null) &&
+                             (!"".equals(valueMatcher.group(5))) &&
+                             (valueMatcher.group(6) != null) &&
+                             (!"".equals(valueMatcher.group(6))))
+                    {
+                        // found a key value pair. Get some of them and integrate them into the hash 
+                        String key = valueMatcher.group(5);
+                        
+                        if ("alignmentX".equals(key) || "alignmentY".equals(key)) {
+                            elementHash += Float.parseFloat(valueMatcher.group(6));
+                        }
+                    }
+                }
+            }
+
+            currentGuiElementSpec.setName("unknown(" + ((int) elementHash) + ")");
+            currentGuiElementSpec.setType(type);
+            currentGuiElementSpec.setIndex(-1);
+            currentGuiElementSpec.setElementHash((int) elementHash);
+        }
+        catch (Exception e)
+        {
+            throw new SAXException("could not parse target", e);
+        }
+    }
+    
+    /**
+     * <p>
+     * creates a default event filter that ignores focus changes, mouse pressed and mouse released
+     * events.
+     * </p>
+     */
+    /*private void setupDefaultEventFilter() {
+        eventFilter = new HashSet<JFCEventId>();
+        eventFilter.add(JFCEventId.MOUSE_PRESSED);
+        eventFilter.add(JFCEventId.MOUSE_RELEASED);
+        eventFilter.add(JFCEventId.FOCUS_GAINED);
+    }*/
+}
Index: /trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCComponentTree.java
===================================================================
--- /trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCComponentTree.java	(revision 963)
+++ /trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCComponentTree.java	(revision 963)
@@ -0,0 +1,289 @@
+//   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.jfc.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;
+
+/**
+ * <p>
+ * This class provides an the interfaces for component trees.
+ * </p>
+ * <p>
+ * The component tree represents the hierarchical structure of the components "as it is" currently during
+ * a session. It may change during the session due to creation and destruction of components.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @author Fabian Glaser
+ * @version 1.0
+ */
+public class JFCComponentTree {
+
+    /**
+     * <p>
+     * Map of all GUI element specifications that are part of the tree for efficient searching. The
+     * keys of the map are the hash values of the GUI elements.
+     * </p>
+     */
+    private Map<Long, JFCGUIElementSpec> guiElementSpecs;
+
+    /**
+     * <p>
+     * Map of all children of GUI elements that are part of the tree. The keys of the map are the
+     * hash values of the parent GUI elements.
+     * </p>
+     */
+    private Map<Long, List<JFCGUIElementSpec>> childRelations;
+
+    /**
+     * <p>
+     * Map of all parents of GUI elements that are part of the tree. The keys of the map are the
+     * hash values of the child GUI elements.
+     * </p>
+     */
+    private Map<Long, JFCGUIElementSpec> parentRelations;
+
+    /**
+     * <p>
+     * the internally created GUI model
+     * </p>
+     */
+    private GUIModel guiModel = new GUIModel();
+
+    /**
+     * <p>
+     * the GUI element factory used in the model
+     * </p>
+     */
+    private IGUIElementFactory guiElementFactory = GUIElementFactory.getInstance();
+
+    /**
+     * <p>
+     * Map of all GUI elements that are part of the tree for efficient searching. The keys of the
+     * map are the hash values of the GUI elements.
+     * </p>
+     */
+    private Map<Long, JFCGUIElement> guiElements;
+
+    /**
+     * <p>
+     * Creates a new JFCComponentTree.
+     * </p>
+     */
+    public JFCComponentTree() {
+        guiElementSpecs = new HashMap<Long, JFCGUIElementSpec>();
+        childRelations = new HashMap<Long, List<JFCGUIElementSpec>>();
+        parentRelations = new HashMap<Long, JFCGUIElementSpec>();
+        guiElements = new HashMap<Long, JFCGUIElement>();
+    }
+
+    /**
+     * <p>
+     * Adds a new component to the tree.
+     * </p>
+     * 
+     * @param parentHash
+     *            hash of the parent window
+     * @param componentHash
+     *            hash of the window to be created
+     * @param componentSpec
+     * 			  the component specification
+     */
+    public void add(Long parentHash,
+                    Long componentHash,
+                    JFCGUIElementSpec componentSpec)
+    {
+        JFCGUIElement guiElement = guiElements.get(componentHash);
+        
+        if (guiElement == null) {
+        	JFCGUIElementSpec parent = guiElementSpecs.get(parentHash);
+            if (parent != null) {
+                List<JFCGUIElementSpec> otherChildren = childRelations.get(parentHash);
+
+                if (otherChildren == null) {
+                    otherChildren = new ArrayList<JFCGUIElementSpec>();
+                    childRelations.put(parentHash, otherChildren);
+                }
+
+                otherChildren.add(componentSpec);
+
+                parentRelations.put(componentHash, parent);
+            }
+            guiElementSpecs.put(componentHash, componentSpec);
+            
+            List<JFCGUIElementSpec> guiElementPath = new ArrayList<JFCGUIElementSpec>();
+
+            while (componentSpec != null) {
+                guiElementPath.add(0, componentSpec);
+                componentSpec = parentRelations.get(componentSpec.getElementHash());
+            }
+
+            try {
+                guiElement =
+                    (JFCGUIElement) guiModel.integratePath(guiElementPath, guiElementFactory);
+            }
+            catch (GUIModelException e) {
+                throw new RuntimeException("could not instantiate GUI element with id " + componentHash, e);
+            }
+            guiElements.put(componentHash, guiElement);
+        }
+    }
+
+    /**
+     * <p>
+     * Searches the tree for a component with the specified hash and returns its
+     * {@link JFCGUIElementSpec} .
+     * </p>
+     * 
+     * @param hash
+     *            hash that is looked for
+     * @return {@link JFCGUIElementSpec} of the window with the given hash if found, null otherwise
+     */
+    public JFCGUIElement find(long hash) {
+        JFCGUIElement guiElement = guiElements.get(hash);
+        if (guiElement == null) {
+            List<JFCGUIElementSpec> guiElementPath = new ArrayList<JFCGUIElementSpec>();
+
+            JFCGUIElementSpec child = guiElementSpecs.get(hash);
+
+            if (child == null) {
+                throw new RuntimeException("no GUI element found with hash " + hash);
+            }
+
+            while (child != null) {
+                guiElementPath.add(0, child);
+                child = parentRelations.get(child.getElementHash());
+            }
+
+            try {
+                guiElement =
+                    (JFCGUIElement) guiModel.integratePath(guiElementPath, guiElementFactory);
+            }
+            catch (GUIModelException e) {
+                throw new RuntimeException("could not instantiate GUI element with id " + hash, e);
+            }
+            guiElements.put(hash, guiElement);
+        }
+        return guiElement;
+    }
+
+    /**
+     * <p>
+     * Sets the name of a GUI element given its hash.
+     * </p>
+     * 
+     * @param hash
+     *            hash of the GUI element
+     * @param windowName
+     *            new name of the GUI element
+     */
+    public void setName(long hash, String windowName) {
+        JFCGUIElementSpec child = guiElementSpecs.get(hash);
+        if (child != null) {
+            child.setName(windowName);
+
+            JFCGUIElement guiElement = guiElements.remove(hash);
+            if (guiElement == null) {
+                // we need to update the GUI model as well
+                find(hash);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Removes a window (defined by its hash) from the tree. All children of the window will be
+     * removed recursively.
+     * </p>
+     * 
+     * @param hash
+     *            hash of the window to be removed
+     * @return number of windows that were removed
+     */
+    public int remove(long hash) {
+        JFCGUIElementSpec node = guiElementSpecs.remove(hash);
+        int removedCounter = 1;
+
+        if (node != null) {
+            List<JFCGUIElementSpec> nodesToBeRemoved = childRelations.remove(hash);
+
+            // remove all children and sub-children, if any
+            if (nodesToBeRemoved != null) {
+                for (int i = 0; i < nodesToBeRemoved.size(); i++) {
+                    JFCGUIElementSpec nodeToBeRemoved = nodesToBeRemoved.get(i);
+                    List<JFCGUIElementSpec> children =
+                        childRelations.remove(nodeToBeRemoved.getElementHash());
+
+                    if (children != null) {
+                        nodesToBeRemoved.addAll(children);
+                    }
+
+                    guiElementSpecs.remove(nodeToBeRemoved.getElementHash());
+                    parentRelations.remove(nodeToBeRemoved.getElementHash());
+                    removedCounter++;
+                }
+            }
+
+            // the node may be a child node of a parent. So search for it and remove it
+            JFCGUIElementSpec parent = parentRelations.remove(hash);
+            if (parent != null) {
+                List<JFCGUIElementSpec> children = childRelations.get(parent.getElementHash());
+
+                if (children != null) {
+                    for (int i = 0; i < children.size(); i++) {
+                        if (children.get(i).getElementHash() == hash) {
+                            children.remove(i);
+                            break;
+                        }
+                    }
+
+                    if (children.size() <= 0) {
+                        childRelations.remove(parent.getElementHash());
+                    }
+                }
+            }
+        }
+        return removedCounter;
+    }
+
+    /**
+     * @return the guiModel
+     */
+    public GUIModel getGUIModel() {
+        return guiModel;
+    }
+
+    /**
+     * <p>
+     * Returns the number of nodes contained in the JFCComponentTree.
+     * </p>
+     * 
+     * @return number of nodes
+     */
+    public int size() {
+        return guiElementSpecs.size();
+    }
+
+}
