Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerationRule.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerationRule.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerationRule.java	(revision 922)
@@ -0,0 +1,866 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jdom.Element;
+import org.jdom.Namespace;
+
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessageType;
+
+/**
+ * <p>
+ * This class defines rules for the generation of MFC events.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold, Patrick Harms
+ */
+class EventGenerationRule {
+
+    /**
+     * <p>
+     * Namespace used for parsing the rule.
+     * </p>
+     */
+    private Namespace namespace;
+
+    /**
+     * <p>
+     * Name of the rule.
+     * </p>
+     */
+    private String name;
+
+    /**
+     * <p>
+     * List of conditions for the rule to be matched.
+     * </p>
+     */
+    private List<MessageCondition> messageConditions;
+
+    /**
+     * <p>
+     * List of parameters to be provided to the generated event.
+     * </p>
+     */
+    private List<Term> eventParameters;
+
+    /**
+     * <p>
+     * List of replay message generation rules.
+     * </p>
+     */
+    private List<ReplayMessageSpec> replayMessageSpecifications;
+
+    /**
+     * <p>
+     * Constructor. Creates a new EventGenerationRule.
+     * </p>
+     * 
+     * @param ruleElement
+     *            the JDOM element that descripes the rule
+     * @param rulesNamespace
+     *            the XML namespace the rule is defined in
+     */
+    @SuppressWarnings("unchecked")
+    EventGenerationRule(Element ruleElement, Namespace rulesNamespace) {
+        this.namespace = rulesNamespace;
+
+        this.name = ruleElement.getAttributeValue("name");
+
+        this.messageConditions = new ArrayList<MessageCondition>();
+        this.eventParameters = new ArrayList<Term>();
+        this.replayMessageSpecifications = new ArrayList<ReplayMessageSpec>();
+
+        for (Element child : (List<Element>) ruleElement.getChildren()) {
+            if ("msg".equals(child.getName()) && namespace.equals(child.getNamespace())) {
+                messageConditions.add(new MessageCondition(child));
+            }
+            else if ("idinfo".equals(child.getName()) && namespace.equals(child.getNamespace())) {
+                for (Element parameterElements : (List<Element>) child.getChildren()) {
+                    eventParameters.add(new Term(parameterElements));
+                }
+            }
+            else if ("genMsg".equals(child.getName()) && namespace.equals(child.getNamespace())) {
+                replayMessageSpecifications.add(new ReplayMessageSpec(child));
+            }
+            else if ("genMsgSeq".equals(child.getName()) && namespace.equals(child.getNamespace()))
+            {
+                replayMessageSpecifications.add(new ReplayMessageSpec(child));
+            }
+            else {
+                throw new IllegalArgumentException(
+                                                   "the provided rules can not be parsed: unknown element " +
+                                                       child.getName());
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the name of the rule.
+     * </p>
+     * 
+     * @return the name
+     */
+    String getName() {
+        return name;
+    }
+
+    /**
+     * <p>
+     * Returns the conditions on the matched messages defined by this rule.
+     * </p>
+     * 
+     * @return the message conditions
+     */
+    List<MessageCondition> getMessageConditions() {
+        return messageConditions;
+    }
+
+    /**
+     * <p>
+     * Returns the parameters of the event generated by this rule.
+     * </p>
+     * 
+     * @return the event parameters
+     */
+    List<Term> getEventParameters() {
+        return eventParameters;
+    }
+
+    /**
+     * <p>
+     * Returns the replay specification defined by this rule.
+     * </p>
+     * 
+     * @return the replay specification
+     */
+    List<ReplayMessageSpec> getReplayMessageSpecifications() {
+        return replayMessageSpecifications;
+    }
+
+    /**
+     * <p>
+     * Helper class that describes conditions on the message sequence when matching this rule.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Steffen Herbold, Patrick Harms
+     */
+    class MessageCondition {
+
+        /**
+         * <p>
+         * True, if the condition defines to match several conditions
+         * </p>
+         */
+        private boolean matchMultiple;
+
+        /**
+         * <p>
+         * Type of the message matched by the condition
+         * </p>
+         */
+        private WindowsMessageType messageType;
+
+        /**
+         * <p>
+         * Term conditions associated with the rule condition
+         * </p>
+         */
+        private List<AttributeCondition> attributeConditions;
+
+        /**
+         * <p>
+         * List of messages to be stored, if the message matches, for continuing the rule
+         * application
+         * </p>
+         */
+        private ArrayList<Term> messagesToStore;
+
+        /**
+         * <p>
+         * Constructor. Creates a new MessageCondition.
+         * </p>
+         * 
+         * @param msgChild
+         *            JDOM element that describes the message condition
+         */
+        @SuppressWarnings("unchecked")
+        private MessageCondition(Element msgChild) {
+            this.matchMultiple = "true".equals(msgChild.getAttributeValue("multiple"));
+            this.messageType =
+                WindowsMessageType.parseMessageType(msgChild.getAttributeValue("type"));
+
+            this.attributeConditions = new ArrayList<AttributeCondition>();
+            for (Element childElement : (List<Element>) msgChild.getChildren("equals", namespace)) {
+                attributeConditions.add(new AttributeCondition(childElement));
+            }
+
+            this.messagesToStore = new ArrayList<Term>();
+            for (Element childElement : (List<Element>) msgChild.getChildren("store", namespace)) {
+                messagesToStore.add(new Term(childElement));
+            }
+            for (Element childElement : (List<Element>) msgChild.getChildren("storeSeq", namespace))
+            {
+                messagesToStore.add(new Term(childElement));
+            }
+        }
+
+        /**
+         * <p>
+         * Returns whether a single message is matched to the condition or a whole sequence can be
+         * matched.
+         * </p>
+         * 
+         * @return true if multiple message shall be matched, false if only a single message is
+         *         matched
+         */
+        boolean matchMultiple() {
+            return matchMultiple;
+        }
+
+        /**
+         * <p>
+         * Returns the type of the matched messages.
+         * </p>
+         * 
+         * @return the message type
+         */
+        WindowsMessageType getMessageType() {
+            return messageType;
+        }
+
+        /**
+         * <p>
+         * Returns the attribute conditions of the message condition.
+         * </p>
+         * 
+         * @return the attribute conditions
+         */
+        List<AttributeCondition> getAttributeConditions() {
+            return attributeConditions;
+        }
+
+        /**
+         * <p>
+         * Returns messages, that have eventually been stored as part of the condition.
+         * </p>
+         * 
+         * @return the stored messages
+         */
+        ArrayList<Term> getMessagesToStore() {
+            return messagesToStore;
+        }
+
+    }
+
+    /**
+     * <p>
+     * Helper class that defines attribute conditions for matching messages.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Steffen Herbold, Patrick Harms
+     */
+    class AttributeCondition {
+
+        /**
+         * <p>
+         * Left hand side of the condition.
+         * </p>
+         */
+        private Term leftHandSide;
+
+        /**
+         * <p>
+         * Reft hand side of the condition.
+         * </p>
+         */
+        private Term rightHandSide;
+
+        /**
+         * <p>
+         * Constructor. Creates a new AttributeCondition.
+         * </p>
+         * 
+         * @param conditionElement
+         *            JDOM element that describes the condition
+         */
+        private AttributeCondition(Element conditionElement) {
+            this.leftHandSide = new Term((Element) conditionElement.getChildren().get(0));
+            this.rightHandSide = new Term((Element) conditionElement.getChildren().get(1));
+        }
+
+        /**
+         * <p>
+         * Returns the left hand side of the condition.
+         * </p>
+         * 
+         * @return the left hand side
+         */
+        Term getLeftHandSide() {
+            return leftHandSide;
+        }
+
+        /**
+         * <p>
+         * Returns the right hand side of the condition.
+         * </p>
+         * 
+         * @return the right hand side
+         */
+        Term getRightHandSide() {
+            return rightHandSide;
+        }
+
+    }
+
+    /**
+     * <p>
+     * Helper class that defines terms to define conditions.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Steffen Herbold, Patrick Harms
+     */
+    class Term {
+
+        /**
+         * <p>
+         * Name of the term.
+         * </p>
+         */
+        private String name;
+
+        /**
+         * <p>
+         * Value of the term, if it is a constValue; null otherwise.
+         * </p>
+         */
+        private String value;
+
+        /**
+         * <p>
+         * Variable name of the object, i.e. a message, of which a parameter is identified if the
+         * term is a winInfoValue or a msgInfoValue; null otherwise.
+         * </p>
+         */
+        private String messageId;
+
+        /**
+         * <p>
+         * Name of the parameter of the object, i.e., a message, of which a parameter is identified
+         * if the term is a paramValue; null otherwise.
+         * </p>
+         */
+        private String messageParameterName;
+
+        /**
+         * <p>
+         * Variable name of the message sequence denoted by the term in case of a seqValue; null
+         * otherwise.
+         * </p>
+         */
+        private String sequenceId;
+
+        /**
+         * <p>
+         * Name of the parameter of the sequence of which a parameter is identified if the term is a
+         * seqValue; null otherwise.
+         * </p>
+         */
+        private String sequenceParameterName;
+
+        /**
+         * <p>
+         * Name of the parameter of the window of the object, e.g. a message, of which a parameter
+         * is identified if the term is a winInfoValue; null otherwise.
+         * </p>
+         */
+        private String windowParameterName;
+
+        /**
+         * <p>
+         * Name of the info of the message of which a parameter is identified if the term is a
+         * msgInfoValue; null otherwise.
+         * </p>
+         */
+        private String messageInfoName;
+
+        /**
+         * <p>
+         * Name of the parameter of the message into which a value shall be stored if the term is a
+         * resolveHwnd, null otherwise
+         * </p>
+         */
+        private String storeParameterName;
+
+        /**
+         * <p>
+         * List of handles to be resolved in case the term is a store or storeSeq; null otherwise.
+         * </p>
+         */
+        private List<Term> resolveHandles;
+
+        /**
+         * <p>
+         * Constructor. Creates a new Term.
+         * </p>
+         * 
+         * @param termElement
+         *            JDOM element that describes the term
+         */
+        @SuppressWarnings("unchecked")
+        private Term(Element termElement) {
+            this.name = termElement.getName();
+
+            if ("constValue".equals(name)) {
+                this.value = termElement.getAttributeValue("value");
+            }
+            else if ("paramValue".equals(name)) {
+                this.messageId = termElement.getAttributeValue("obj");
+                this.messageParameterName = termElement.getAttributeValue("param");
+            }
+            else if ("winInfoValue".equals(name)) {
+                this.messageId = termElement.getAttributeValue("obj");
+                this.windowParameterName = termElement.getAttributeValue("winParam");
+            }
+            else if ("msgInfoValue".equals(name)) {
+                this.messageId = termElement.getAttributeValue("obj");
+                this.messageInfoName = termElement.getAttributeValue("msgParam");
+            }
+            else if ("seqValue".equals(name)) {
+                this.sequenceId = termElement.getAttributeValue("seqObj");
+                this.sequenceParameterName = termElement.getAttributeValue("param");
+            }
+            else if ("store".equals(name)) {
+                this.messageId = termElement.getAttributeValue("var");
+                if ((termElement.getChildren() != null) && (termElement.getChildren().size() > 0)) {
+                    this.resolveHandles = new ArrayList<Term>();
+                    for (Element child : (List<Element>) termElement.getChildren()) {
+                        this.resolveHandles.add(new Term(child));
+                    }
+                }
+            }
+            else if ("storeSeq".equals(name)) {
+                this.sequenceId = termElement.getAttributeValue("varSeq");
+                if ((termElement.getChildren() != null) && (termElement.getChildren().size() > 0)) {
+                    this.resolveHandles = new ArrayList<Term>();
+                    for (Element child : (List<Element>) termElement.getChildren()) {
+                        this.resolveHandles.add(new Term(child));
+                    }
+                }
+            }
+            else if ("resolveHwnd".equals(name)) {
+                this.messageParameterName = termElement.getAttributeValue("param");
+                this.storeParameterName = termElement.getAttributeValue("storeParam");
+            }
+        }
+
+        /**
+         * <p>
+         * Returns the name of the term.
+         * </p>
+         * 
+         * @return the name
+         */
+        String getName() {
+            return name;
+        }
+
+        /**
+         * <p>
+         * Returns the value of the term.
+         * </p>
+         * 
+         * @return the value
+         */
+        String getValue() {
+            return value;
+        }
+
+        /**
+         * <p>
+         * Returns the object Id of the message, which is resolved as part of this term.
+         * </p>
+         * 
+         * @return the object Id
+         */
+        String getMessageId() {
+            return messageId;
+        }
+
+        /**
+         * <p>
+         * Returns the name of the message parameter that is resolved as part of this term.
+         * </p>
+         * 
+         * @return the message parameter name
+         */
+        String getMessageParameterName() {
+            return messageParameterName;
+        }
+
+        /**
+         * <p>
+         * Returns the object Id of the message sequence, which is resolved as part of this term.
+         * </p>
+         * 
+         * @return the object Id
+         */
+        String getSequenceId() {
+            return sequenceId;
+        }
+
+        /**
+         * <p>
+         * Returns the name of the message parameter that is resolved as part of this term.
+         * </p>
+         * 
+         * @return the sequenceParameter
+         */
+        String getSequenceParameterName() {
+            return sequenceParameterName;
+        }
+
+        /**
+         * <p>
+         * Returns the window parameter name that is resolved as part of this term.
+         * </p>
+         * 
+         * @return the name of the window parameter
+         */
+        String getWindowParameterName() {
+            return windowParameterName;
+        }
+
+        /**
+         * <p>
+         * Returns the name of the message info value that is resolved as part of this term.
+         * </p>
+         * 
+         * @return the name of the message info value
+         */
+        String getMessageInfoName() {
+            return messageInfoName;
+        }
+
+        /**
+         * <p>
+         * Returns the object Id under which a message will be stored.
+         * </p>
+         * 
+         * @return the object Id
+         */
+        String getStoreParameterName() {
+            return storeParameterName;
+        }
+
+        /**
+         * <p>
+         * Returns all terms that are responsible to resolve HWNDs.
+         * </p>
+         * 
+         * @return the terms
+         */
+        List<Term> getResolveHandles() {
+            return resolveHandles;
+        }
+
+    }
+
+    /**
+     * <p>
+     * Helper class that defines the replay specification part of rules.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Steffen Herbold, Patrick Harms
+     */
+    class ReplayMessageSpec {
+
+        /**
+         * <p>
+         * Determines if this specification defines one, or a sequence of messages.
+         * </p>
+         */
+        private boolean generateSingleMessage;
+
+        /**
+         * <p>
+         * Object Id of a concrete message of message sequence to be replayed as is.
+         * </p>
+         */
+        private String replayObjectId;
+
+        /**
+         * <p>
+         * Term describing the type of the generated message.
+         * </p>
+         */
+        private Term type;
+
+        /**
+         * <p>
+         * Term describing the target of the generated message.
+         * </p>
+         */
+        private Term target;
+
+        /**
+         * <p>
+         * Term describing the LO word of the LParam of the generated message.
+         * </p>
+         */
+        private Term lparamLoWord;
+
+        /**
+         * <p>
+         * Term describing the HI word of the LParam of the generated message.
+         * </p>
+         */
+        private Term lparamHiWord;
+
+        /**
+         * <p>
+         * Term describing the LParam of the generated message.
+         * </p>
+         */
+        private Term lparam;
+
+        /**
+         * <p>
+         * Term describing the LO word of the WParam of the generated message.
+         * </p>
+         */
+        private Term wparamLoWord;
+
+        /**
+         * <p>
+         * Term describing the HI word of the WParam of the generated message.
+         * </p>
+         */
+        private Term wparamHiWord;
+
+        /**
+         * <p>
+         * Term describing the WParam of the generated message.
+         * </p>
+         */
+        private Term wparam;
+
+        /**
+         * <p>
+         * Value in milliseconds that the replay waits until the the next message is replayed.
+         * </p>
+         */
+        private int delay;
+
+        /**
+         * <p>
+         * Constructor. Creates a new ReplayMessageSpec.
+         * </p>
+         * 
+         * @param replayMessageSpecElement
+         *            JDOM element that describes the replay message specification
+         */
+        @SuppressWarnings("unchecked")
+        private ReplayMessageSpec(Element replayMessageSpecElement) {
+            List<Element> children = replayMessageSpecElement.getChildren();
+            if ("genMsg".equals(replayMessageSpecElement.getName())) {
+                generateSingleMessage = true;
+                if (children.size() == 1) {
+                    replayObjectId = children.get(0).getAttributeValue("obj");
+                }
+            }
+            else {
+                generateSingleMessage = false;
+                if (children.size() == 1) {
+                    replayObjectId = children.get(0).getAttributeValue("seqObj");
+                }
+            }
+
+            this.delay = Integer.parseInt(replayMessageSpecElement.getAttributeValue("delay"));
+
+            if (children.size() > 1) {
+                for (Element child : children) {
+                    Element termElement = (Element) child.getChildren().get(0);
+
+                    if (child.getName().equals("type")) {
+                        this.type = new Term(termElement);
+                    }
+                    else if (child.getName().equals("target")) {
+                        this.target = new Term(termElement);
+
+                        if (!generateSingleMessage) {
+                            // in this case, the target is always a sequence value term, i.e.
+                            // the targets of the originally recorded sequence. So the
+                            // replay object id is set to this sequence
+                            replayObjectId = target.getSequenceId();
+                        }
+                    }
+                    else if (child.getName().equals("LPARAM")) {
+                        Element loWordElement = child.getChild("LOWORD", namespace);
+                        if (loWordElement != null) {
+                            this.lparamLoWord =
+                                new Term((Element) loWordElement.getChildren().get(0));
+                        }
+
+                        Element hiWordElement = child.getChild("HIWORD", namespace);
+                        if (hiWordElement != null) {
+                            this.lparamHiWord =
+                                new Term((Element) hiWordElement.getChildren().get(0));
+                        }
+
+                        if ((lparamLoWord == null) && (lparamHiWord == null)) {
+                            this.lparam = new Term(termElement);
+                        }
+                    }
+                    else if (child.getName().equals("WPARAM")) {
+                        Element loWordElement = child.getChild("LOWORD", namespace);
+                        if (loWordElement != null) {
+                            this.wparamLoWord =
+                                new Term((Element) loWordElement.getChildren().get(0));
+                        }
+
+                        Element hiWordElement = child.getChild("HIWORD", namespace);
+                        if (hiWordElement != null) {
+                            this.wparamHiWord =
+                                new Term((Element) hiWordElement.getChildren().get(0));
+                        }
+
+                        if ((wparamLoWord == null) && (wparamHiWord == null)) {
+                            this.wparam = new Term(termElement);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * <p>
+         * Determines if this specification defines one, or a sequence of messages.
+         * </p>
+         * 
+         * @return true if only a single message is generated; false if a sequence is generated
+         */
+        boolean generateSingleMessage() {
+            return generateSingleMessage;
+        }
+
+        /**
+         * <p>
+         * Returns the object Id from which the message is generated.
+         * </p>
+         * 
+         * @return the object Id
+         */
+        String getReplayObjectId() {
+            return replayObjectId;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the type of the generated message.
+         * </p>
+         * 
+         * @return the type term
+         */
+        Term getType() {
+            return type;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the target of the generated message.
+         * </p>
+         * 
+         * @return the target term
+         */
+        Term getTarget() {
+            return target;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the LO word of the LParam of the generated message.
+         * </p>
+         * 
+         * @return the LParam LO word term
+         */
+        Term getLparamLoWord() {
+            return lparamLoWord;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the HI word of the LParam of the generated message.
+         * </p>
+         * 
+         * @return the LParam HI word term
+         */
+        Term getLparamHiWord() {
+            return lparamHiWord;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the LParam of the generated message.
+         * </p>
+         * 
+         * @return the LParam term
+         */
+        Term getLparam() {
+            return lparam;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the LO word of the WParam of the generated message.
+         * </p>
+         * 
+         * @return the WParam LO word term
+         */
+        Term getWparamLoWord() {
+            return wparamLoWord;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the HI word of the WParam of the generated message.
+         * </p>
+         * 
+         * @return the WParam HI word term
+         */
+        Term getWparamHiWord() {
+            return wparamHiWord;
+        }
+
+        /**
+         * <p>
+         * Returns the term that describes the WParam of the generated message.
+         * </p>
+         * 
+         * @return the WParam term
+         */
+        Term getWparam() {
+            return wparam;
+        }
+
+        /**
+         * <p>
+         * Returns the delay during the replay after this message is sent.
+         * </p>
+         * 
+         * @return the delay
+         */
+        int getDelay() {
+            return delay;
+        }
+
+    }
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerator.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerator.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerator.java	(revision 922)
@@ -0,0 +1,1114 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.logging.Level;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.Namespace;
+import org.jdom.input.SAXBuilder;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.plugin.mfc.EventGenerationRule.Term;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.MFCEventTypeFactory;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.ReplayWindowsMessage;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessage;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessageType;
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCGUIElement;
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Translates sequences of windows messages into {@link Event}s that can be used by the
+ * QUEST core libraries.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold, Patrick Harms
+ */
+public class EventGenerator {
+
+    /**
+     * <p>
+     * the list of all event generation rules available
+     * </p>
+     */
+    private List<EventGenerationRule> generationRules;
+    
+    /**
+     * <p>
+     * Name and path of the XML files containing the rules.
+     * </p>
+     */
+    private String rulesFile;
+
+    /**
+     * <p>
+     * Iterator used for the current sequence.
+     * </p>
+     */
+    private ListIterator<WindowsMessage> sequenceIterator;
+
+    /**
+     * <p>
+     * Token that is currently being generated.
+     * </p>
+     */
+    private Event currentEvent;
+
+    /**
+     * <p>
+     * Event type of the current token. Stored as a member to be able to update it during the
+     * parsing of the idinfo tag.
+     * </p>
+     */
+    private IEventType currentType;
+
+    /**
+     * <p>
+     * 
+     * </p>
+     */
+    private MFCGUIElement currentTarget;
+
+    /**
+     * <p>
+     * Reference to the ul:rules namespace.
+     * </p>
+     */
+    private static Namespace rulesNamespace;
+
+    /**
+     * <p>
+     * The name of the rule that is currently being evaluated.
+     * </p>
+     */
+    private String currentRuleName;
+
+    /**
+     * <p>
+     * Internal message storage. Used to implement the <code>{@literal <store>}</code> and
+     * <code>{@literal <storeSeq>}</code> tags.
+     * </p>
+     */
+    private Map<String, Object> messageStorage;
+
+    /**
+     * <p>
+     * reference to the window tree created during parsing
+     * </p>
+     */
+    private WindowTree windowTree;
+
+    /**
+     * <p>
+     * Creates a new EventGenerator. Sets "data/rules.xml" as default file for the rules.
+     * </p>
+     */
+    public EventGenerator(WindowTree windowTree) {
+        rulesFile = "data/rules.xml";
+        this.windowTree = windowTree;
+    }
+
+    /**
+     * <p>
+     * Tries to match the rules to the given sequence to generate an {@link Event}.
+     * </p>
+     * <p>
+     * The rules are matched the order, in which they are defined in the XML file. Therefore, the
+     * order of the rules in the file defines priorities, when multiple rules could be matched to
+     * the same sequence.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence of message for which an event will be generated
+     * @return event that matches the messages; null, if no rule can be matched
+     */
+    public Event generateEvent(List<WindowsMessage> sequence) {
+        if (generationRules == null) {
+            parseGenerationRules();
+        }
+        
+        
+        /*System.out.println("generating event for ");
+        for (WindowsMessage msg : sequence) {
+            System.out.println("    " + msg + "  " + msg.getParameter("scrollBarHandle") + "  " + msg.getTargetXML());
+        }*/
+
+        boolean isMatch = false;
+
+        for (int ruleIndex = 0; ruleIndex < generationRules.size() && !isMatch; ruleIndex++) {
+            EventGenerationRule currentRule = generationRules.get(ruleIndex);
+            currentRuleName = currentRule.getName();
+            
+            //System.out.println("checking rule " + currentRuleName);
+            
+            messageStorage = new HashMap<String, Object>();
+            sequenceIterator = sequence.listIterator();
+            
+            isMatch = evaluateMessageConditions(currentRule);
+
+            if (isMatch) {
+                currentType = MFCEventTypeFactory.getInstance().getEventType
+                    (currentRuleName, resolveParameters(currentRule.getEventParameters()));
+                
+                currentEvent = new Event(currentType, currentTarget);
+                
+                for (EventGenerationRule.ReplayMessageSpec replayMessageSpec :
+                      currentRule.getReplayMessageSpecifications())
+                {
+                    if (replayMessageSpec.generateSingleMessage()) {
+                        try {
+                            generateReplayMessage(replayMessageSpec);
+                        }
+                        catch (IllegalArgumentException e) {
+                            Console.printerrln(e.getMessage());
+                            // TODO currentToken.invalidateReplay();
+                        }
+                    }
+                    else {
+                        try {
+                            generateReplaySequence(replayMessageSpec);
+                            // TODO currentToken.invalidateReplay();
+                        }
+                        catch (IllegalArgumentException e) {
+                            Console.printerrln(e.getMessage());
+                            // TODO currentToken.invalidateReplay();
+                        }
+                    }
+                }
+
+                Console.traceln(Level.FINE, currentEvent.getType().toString() + " matched");
+            }
+            else {
+                currentEvent = null;
+            }
+        }
+        if (!isMatch) {
+            Console.traceln(Level.WARNING, "no match found for sequence: " + sequence.toString());
+        }
+        
+        
+        /*if (currentEvent != null && currentEvent.getReplayables() != null) {
+            System.out.println("replay messages are ");
+            for (de.ugoe.cs.autoquest.eventcore.IReplayable msg : currentEvent.getReplayables()) {
+                System.out.println("    " + msg + "  " + msg.getReplay());
+            }
+        }
+
+        System.out.println();*/
+
+
+        return currentEvent;
+    }
+
+    // ////////////////////////////////////////////////////////////
+    // Helper functions for matching of events, i.e., msg-nodes //
+    // ////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Evaluates whether the current message sequence matches a given rule.
+     * </p>
+     *
+     * @param currentRule rule that is matched
+     * @return true if the message sequence matches the rule; false otherwise
+     */
+    private boolean evaluateMessageConditions(EventGenerationRule currentRule) {
+        boolean isMatch = true;
+        List<EventGenerationRule.MessageCondition> messageConditions =
+            currentRule.getMessageConditions();
+
+        int i = 0;
+        while (isMatch && i < messageConditions.size()) {
+            EventGenerationRule.MessageCondition messageCondition = messageConditions.get(i);
+            if (messageCondition.matchMultiple()) {
+                EventGenerationRule.MessageCondition nextMessageCondition = null;
+                if (i + 1 < messageConditions.size()) {
+                    nextMessageCondition = messageConditions.get(i + 1);
+                }
+                try {
+                    isMatch = matchMultipleConditions(messageCondition, nextMessageCondition);
+                }
+                catch (IllegalArgumentException e) {
+                    Console.printerrln(e.getMessage());
+                }
+            }
+            else {
+                try {
+                    isMatch = matchSingleMessage(messageCondition);
+                }
+                catch (IllegalArgumentException e) {
+                    Console.printerrln(e.getMessage());
+                }
+            }
+            i++;
+        }
+        
+        return isMatch;
+    }
+
+    /**
+     * <p>
+     * Handles msg-nodes where multiple is not true, i.e., not a sequences.
+     * </p>
+     * 
+     * @param messageElement
+     *            {@link Element} representing the msg-node
+     * @return true, if a match is found; false otherwise
+     */
+    private boolean matchSingleMessage(EventGenerationRule.MessageCondition condition) {
+        boolean isMatch = false;
+        WindowsMessage currentMessage = null;
+
+        WindowsMessageType type = condition.getMessageType();
+
+        while (!isMatch && sequenceIterator.hasNext()) {
+            /*
+             * traverses the messages from the current position forward till a message with the
+             * correct type is found
+             */
+            currentMessage = sequenceIterator.next();
+            if (type == currentMessage.getType()) {
+                // message with the correct type found
+                // eval child nodes for further matching/storing
+                isMatch = evaluateMessageCondition(currentMessage, condition);
+
+                // in case the message is a match, eval storage children
+                if (isMatch) {
+                    handleStorage(condition, currentMessage);
+                    currentTarget = currentMessage.getTarget();
+                    // TODO currentToken.setTargetShort(currentMessage.getParentNames());
+                }
+            }
+        }
+
+        return isMatch;
+    }
+
+    /**
+     * <p>
+     * Handles msg-nodes where multiple is true, i.e., sequences. Requires knowledge about the next
+     * msg-node to determine the end of the sequence.
+     * </p>
+     * 
+     * @param messageElement
+     *            {@link Element} representing the msg-node
+     * @param nextMessageElement
+     *            {@link Element} representing the next msg-node; {@code null} if the current node
+     *            is the last one
+     * @return true, if a sequence is matched; false otherwise
+     */
+    private boolean matchMultipleConditions(EventGenerationRule.MessageCondition condition,
+                                            EventGenerationRule.MessageCondition nextCondition)
+    {
+        boolean isMatch = false;
+        boolean isCurrentMatch = false;
+        boolean nextMatchFound = false;
+        WindowsMessage currentMessage = null;
+        WindowsMessage nextMessage = null;
+
+        WindowsMessageType type = condition.getMessageType();
+
+        WindowsMessageType nextType = null;
+        if (nextCondition != null) {
+            nextType = nextCondition.getMessageType();
+        }
+
+        while (!nextMatchFound && sequenceIterator.hasNext()) {
+            currentMessage = sequenceIterator.next();
+            if (type == currentMessage.getType()) {
+                isCurrentMatch = evaluateMessageCondition(currentMessage, condition);
+                isMatch = isMatch || isCurrentMatch;
+
+                if (isCurrentMatch) {
+                    handleStorage(condition, currentMessage);
+                    currentTarget = currentMessage.getTarget();
+                    // TODO currentToken.setTargetShort(currentMessage.getParentNames());
+                }
+            }
+            if (nextCondition != null && isMatch) {
+                // peek next message to check if the sequence ends and the next
+                // match is found
+                if (!sequenceIterator.hasNext()) {
+                    return false; // sequence is over, but not all messages are
+                                  // found
+                }
+                nextMessage = sequenceIterator.next();
+                sequenceIterator.previous();
+
+                if (nextType == nextMessage.getType()) {
+                    nextMatchFound = evaluateMessageCondition(nextMessage, nextCondition);
+                }
+
+            }
+        }
+
+        return isMatch;
+    }
+
+    /**
+     * <p>
+     * Handles equals-nodes.
+     * </p>
+     * 
+     * @param currentMessage
+     *            {@link Element} representing the msg-node the equals-node belongs to
+     * @param messageElement
+     *            {@link Element} representing the equals-node to be evaluated
+     * @return true, if constraint is fulfilled; false otherwise
+     */
+    private boolean evaluateMessageCondition(WindowsMessage                       currentMessage,
+                                             EventGenerationRule.MessageCondition condition)
+    {
+        boolean isMatch = true;
+        for (int i = 0; isMatch && (i < condition.getAttributeConditions().size()); i++)
+        {
+            EventGenerationRule.AttributeCondition attrCond =
+                condition.getAttributeConditions().get(i);
+            
+            // the size 2 of termElements is guaranteed by the XML schema
+            Object value1 = getTermValue(currentMessage, attrCond.getLeftHandSide(), Object.class);
+            Object value2 = getTermValue(currentMessage, attrCond.getRightHandSide(), Object.class);
+            if (value1 == null || value2 == null) {
+                isMatch = false;
+            }
+            else {
+                isMatch = isMatch && value1.equals(value2);
+            }
+        }
+//        for (Element childElement : (List<Element>) messageElement.getChildren("equalsSeq",
+//                                                                               rulesNamespace))
+//        {
+//            List<Element> termElements = childElement.getChildren();
+//            List<String> values1 = getTermValueSeq(termElements.get(0));
+//            List<String> values2 = getTermValueSeq(termElements.get(0));
+//            if (values1 == null || values2 == null) {
+//                isMatch = false;
+//            }
+//            else {
+//                isMatch = isMatch && values1.equals(values2);
+//            }
+//        }
+        return isMatch;
+    }
+
+    /**
+     * <p>
+     * Handles store-nodes and storeSeq-nodes.
+     * </p>
+     * 
+     * @param messageElement
+     *            {@link Element} representing the msg-node that is currently being evaluated
+     * @param currentMessage
+     *            current message in the message sequence that is matched; this is the message that
+     *            is stored
+     */
+    @SuppressWarnings("unchecked")
+    private void handleStorage(EventGenerationRule.MessageCondition messageCondition,
+                               WindowsMessage                       currentMessage)
+    {
+        for (Term valueToStore : messageCondition.getMessagesToStore())
+        {
+            if (valueToStore.getMessageId() != null) {
+              ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage);
+              messageStorage.put(valueToStore.getMessageId(), replayMessage);
+              resolveHwnd(replayMessage, valueToStore.getResolveHandles());
+            }
+            else if (valueToStore.getSequenceId() != null) {
+                Object tmp = messageStorage.get(valueToStore.getSequenceId());
+                ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage);
+                List<ReplayWindowsMessage> storedSequence;
+                if (tmp == null || tmp instanceof ReplayWindowsMessage) {
+                    storedSequence = new LinkedList<ReplayWindowsMessage>();
+                    storedSequence.add(replayMessage);
+                    messageStorage.put(valueToStore.getSequenceId(), storedSequence);
+                }
+                else if (tmp instanceof List<?>) {
+                    storedSequence = (List<ReplayWindowsMessage>) tmp;
+                    storedSequence.add(replayMessage);
+                    messageStorage.put(valueToStore.getSequenceId(), storedSequence);
+                }
+                resolveHwnd(replayMessage, valueToStore.getResolveHandles());
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Resolves a parameter that contains a HWND of a message to the target string of the HWND and
+     * stores it.
+     * </p>
+     * 
+     * @param currentMessage
+     *            message whose HWND is resolved
+     * @param list
+     *            child element of the store node that represents the resolve
+     */
+    private void resolveHwnd(ReplayWindowsMessage currentMessage, List<Term> resolveHandles) {
+        if (resolveHandles != null) {
+            for (Term resolveElement : resolveHandles) {
+                String param = resolveElement.getMessageParameterName();
+                String storeParam = resolveElement.getStoreParameterName();
+                long paramHwnd = (Long) currentMessage.getParameter(param);
+                MFCGUIElement guiElement = windowTree.find(paramHwnd);
+                if (guiElement != null) {
+                    currentMessage.addParameter(storeParam, "" + guiElement.toXML());
+                }
+            }
+        }
+    }
+
+    // /////////////////////////////////////////////////////
+    // Helper functions for generating the replay, i.e.,
+    // parsing of genMsg und genMsgSeq-nodes
+    // /////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Handles genMsg-nodes and adds the replay to the {@link Event} that is generated.
+     * </p>
+     * 
+     * @param genMsgElement
+     *            {@link Element} representing the genMsg-node
+     */
+    private void generateReplayMessage(EventGenerationRule.ReplayMessageSpec messageSpec) {
+        ReplayWindowsMessage generatedMessage = null;
+        if (messageSpec.getReplayObjectId() != null) { // replay stored message without change
+            generatedMessage = getStoredMessageVariable(null, messageSpec.getReplayObjectId());
+        }
+        else { // generate message according to the rule
+            generatedMessage = new ReplayWindowsMessage
+                (getTermValue(messageSpec.getType(), WindowsMessageType.class));
+            generatedMessage.setTargetXML(getTermValue(messageSpec.getTarget(), String.class));
+
+            if ((messageSpec.getLparamHiWord() != null) ||
+                (messageSpec.getLparamLoWord() != null))
+            {
+                generatedMessage.setLPARAM
+                    (loHiWord(messageSpec.getLparamLoWord(), messageSpec.getLparamHiWord()));
+            }
+            else if (messageSpec.getLparam() != null) {
+                try {
+                    generatedMessage.setLPARAM(getTermValue(messageSpec.getLparam(), Long.class));
+                }
+                catch (IllegalArgumentException e) {
+                    generatedMessage.setLPARAMasWindowDesc
+                        (getTermValue(messageSpec.getLparam(), String.class));
+                }
+            }
+
+            if ((messageSpec.getWparamHiWord() != null) ||
+                (messageSpec.getWparamLoWord() != null))
+            {
+                generatedMessage.setWPARAM
+                    (loHiWord(messageSpec.getWparamLoWord(), messageSpec.getWparamHiWord()));
+            }
+            else if (messageSpec.getWparam() != null) {
+                try {
+                    generatedMessage.setWPARAM(getTermValue(messageSpec.getWparam(), Long.class));
+                }
+                catch (IllegalArgumentException e) {
+                    generatedMessage.setWPARAMasWindowDesc
+                        (getTermValue(messageSpec.getWparam(), String.class));
+                }
+            }
+            
+            
+        }
+
+        generatedMessage.setDelay(messageSpec.getDelay());
+
+        currentEvent.addReplayable(generatedMessage);
+    }
+
+    /**
+     * Handles genMsgSeq-nodes and adds the replay to the {@link Event} that is generated.</p>
+     * 
+     * @param genMsgElement
+     *            {@link Element} representing the genMsgSeq-node.
+     */
+    private void generateReplaySequence(EventGenerationRule.ReplayMessageSpec messageSpec)
+    {
+        List<ReplayWindowsMessage> generatedMessageSeq = new LinkedList<ReplayWindowsMessage>();
+        if (messageSpec.getReplayObjectId() != null) { // replay stored sequence without changes
+            generatedMessageSeq = getStoredSeqVariable(messageSpec.getReplayObjectId());
+        }
+        else {
+            List<ReplayWindowsMessage> seqVar =
+                getStoredSeqVariable(messageSpec.getReplayObjectId());
+            
+            Term typeTerm = messageSpec.getType();
+            if (typeTerm.getSequenceId() != null) {
+                seqVar = getStoredSeqVariable(typeTerm.getSequenceId());
+                for (WindowsMessage msg : seqVar) {
+                    ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(msg.getType());
+                    replayMessage.setDelay(messageSpec.getDelay());
+                    generatedMessageSeq.add(replayMessage);
+                }
+            }
+            else { // constValue type
+                WindowsMessageType constMsgType = getTermValue(typeTerm, WindowsMessageType.class);
+                for (int i = 0; i < seqVar.size(); i++) {
+                    ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(constMsgType);
+                    replayMessage.setDelay(messageSpec.getDelay());
+                    generatedMessageSeq.add(replayMessage);
+                }
+            }
+
+            createSequenceTarget(generatedMessageSeq, messageSpec);
+            createSequenceLParam(generatedMessageSeq, messageSpec);
+            createSequenceWParam(generatedMessageSeq, messageSpec);
+        }
+        
+        currentEvent.addReplayableSequence(generatedMessageSeq);
+    }
+
+    /**
+     * <p>
+     * Creates the targets for replay sequences generated with genMsgSeq-nodes.
+     * </p>
+     * 
+     * @param generatedMessageSeq
+     *            list of the messages that is being generated
+     * @param msgsGenerated
+     *            boolean stating if the list of messages is already generated or if the generation
+     *            has to be handles by this method
+     * @param constMsgType
+     *            a constant message type that is used for message generation, in case the list of
+     *            message is generated by this method
+     * @param termElement
+     *            {@link Element} representing the term-node describing the target
+     * @return true, if the list of message is generated after calling this method; false otherwise
+     * @throws NoSuchElementException
+     *             thrown if the seqVar referred to in the termElement contains a different number
+     *             of messages than is contained in messageSeq
+     */
+    private void createSequenceTarget(List<ReplayWindowsMessage>            generatedMessageSeq,
+                                      EventGenerationRule.ReplayMessageSpec messageSpec)
+        throws NoSuchElementException
+    {
+        Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator();
+        if (messageSpec.getTarget().getSequenceId() != null) {
+            List<ReplayWindowsMessage> seqVar =
+                getStoredSeqVariable(messageSpec.getTarget().getSequenceId());
+
+            if (seqVar.size() != generatedMessageSeq.size()) {
+                throw new IllegalArgumentException
+                    ("Failure generating replay sequence for rule " + currentRuleName +
+                     ": One or more of the sequence variables used to generate a sequence have " +
+                     "different lenghts.");
+            }
+            
+            for (WindowsMessage msg : seqVar) {
+                seqIterator.next().setTarget(msg.getTarget());
+            }
+        }
+        else { // const value
+            throw new AssertionError("target must be a sequence variable!");
+            /*
+             * If target would not be a variable, the message-elements could not yet be created and
+             * the whole sequence might be broken. If this is to be changed, createSequenceLParam
+             * and createSequenceWParam need to be addepted, too.
+             */
+        }
+    }
+
+    /**
+     * <p>
+     * Creates the LPARAMs for replay sequences generated with genMsgSeq-nodes.
+     * </p>
+     * 
+     * @param generatedMessageSeq
+     *            list of the messages that is being generated
+     * @param msgsGenerated
+     *            boolean stating if the list of messages is already generated or if the generation
+     *            has to be handles by this method
+     * @param constMsgType
+     *            a constant message type that is used for message generation, in case the list of
+     *            message is generated by this method
+     * @param termElement
+     *            {@link Element} representing the term-node describing the LPARAM
+     * @return true, if the list of message is generated after calling this method; false otherwise
+     * @throws NoSuchElementException
+     *             thrown if the seqVar referred to in the termElement contains a different number
+     *             of messages than is contained in messageSeq
+     */
+    private void createSequenceLParam(List<ReplayWindowsMessage>            generatedMessageSeq,
+                                      EventGenerationRule.ReplayMessageSpec messageSpec)
+        throws NoSuchElementException
+    {
+        Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator();
+        if (messageSpec.getLparam().getSequenceId() != null) {
+            List<ReplayWindowsMessage> seqVar =
+                getStoredSeqVariable(messageSpec.getLparam().getSequenceId());
+            
+            if (seqVar.size() != generatedMessageSeq.size()) {
+                throw new IllegalArgumentException
+                    ("Failure generating replay sequence for rule " + currentRuleName +
+                     ": One or more of the sequence variables used to generate a sequence have " +
+                     "different lengths.");
+            }
+            for (WindowsMessage msg : seqVar) {
+                ReplayWindowsMessage currentSeqMsg = seqIterator.next();
+                Object paramValue =
+                    msg.getParameter(messageSpec.getLparam().getSequenceParameterName());
+                if (paramValue instanceof Long) {
+                    currentSeqMsg.setLPARAM((Long) paramValue);
+                }
+                else {
+                    currentSeqMsg.setLPARAMasWindowDesc((String) paramValue);
+                }
+            }
+        }
+        else { // const value
+            int paramValue = getTermValue(messageSpec.getLparam(), int.class);
+            while (seqIterator.hasNext()) {
+                seqIterator.next().setLPARAM(paramValue);
+            }
+        }
+
+    }
+
+    /**
+     * <p>
+     * Creates the WPARAMs for replay sequences generated with genMsgSeq-nodes.
+     * </p>
+     * 
+     * @param generatedMessageSeq
+     *            list of the messages that is being generated
+     * @param msgsGenerated
+     *            boolean stating if the list of messages is already generated or if the generation
+     *            has to be handles by this method
+     * @param constMsgType
+     *            a constant message type that is used for message generation, in case the list of
+     *            message is generated by this method
+     * @param termElement
+     *            {@link Element} representing the term-node describing the WPARAM
+     * @return true, if the list of message is generated after calling this method; false otherwise
+     * @throws NoSuchElementException
+     *             thrown if the seqVar referred to in the termElement contains a different number
+     *             of messages than is contained in messageSeq
+     */
+    private void createSequenceWParam(List<ReplayWindowsMessage>            generatedMessageSeq,
+                                      EventGenerationRule.ReplayMessageSpec messageSpec)
+        throws NoSuchElementException
+    {
+        Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator();
+        if (messageSpec.getWparam().getSequenceId() != null) {
+            List<ReplayWindowsMessage> seqVar =
+                getStoredSeqVariable(messageSpec.getWparam().getSequenceId());
+            
+            if (seqVar.size() != generatedMessageSeq.size()) {
+                throw new IllegalArgumentException
+                    ("Failure generating replay sequence for rule " + currentRuleName +
+                     ": One or more of the sequence variables used to generate a sequence have " +
+                     "different lengths.");
+            }
+            for (WindowsMessage msg : seqVar) {
+                ReplayWindowsMessage currentSeqMsg = seqIterator.next();
+                Object paramValue =
+                    msg.getParameter(messageSpec.getWparam().getSequenceParameterName());
+                if (paramValue instanceof Long) {
+                    currentSeqMsg.setWPARAM((Long) paramValue);
+                }
+                else {
+                    currentSeqMsg.setWPARAMasWindowDesc((String) paramValue);
+                }
+            }
+        }
+        else { // const value
+            long paramValue = getTermValue(messageSpec.getWparam(), Long.class);
+            while (seqIterator.hasNext()) {
+                seqIterator.next().setWPARAM(paramValue);
+            }
+        }
+    }
+
+    // ////////////////////////////
+    // General helper functions //
+    // ////////////////////////////
+
+    /**
+     * <p>
+     * Resolves the parameters described by {@link Term}s.
+     * </p>
+     *
+     * @param eventParameters terms whose parameters are resolved
+     * @return resolved parameters
+     */
+    private Map<String, String> resolveParameters(List<Term> eventParameters) {
+        Map<String, String> resultParameters = null;
+        
+        if ((eventParameters != null) && (eventParameters.size() > 0)) {
+            resultParameters = new HashMap<String, String>();
+            
+            for (Term term : eventParameters) {
+                if ("seqValue".equals(term.getName())) {
+                    List<String> values = getTermValueAsList(term, String.class);
+                                        
+                    resultParameters.put
+                        (term.getSequenceParameterName(), (String) values.get(values.size() - 1));
+                }
+                else {
+                    resultParameters.put
+                        (term.getMessageParameterName(), getTermValue(term, String.class));
+                }
+            }
+        }
+        
+        return resultParameters;
+    }
+
+    /**
+     * <p>
+     * Retrieves a message from the storage for, e.g., comparison or replay. "this" is used to refer
+     * to the current message.
+     * </p>
+     * 
+     * @param currentMessage
+     *            current message during the parsing; passed to handle "this"
+     * @param obj
+     *            object identifier in the storage
+     * @return message retrieved from the storage
+     * @throws IllegalArgumentException
+     *             thrown in case of invalid uses of "this" or if no message with the identifier obj
+     *             is found in the storage
+     */
+    private ReplayWindowsMessage getStoredMessageVariable(WindowsMessage currentMessage, String obj)
+        throws IllegalArgumentException
+    {
+        ReplayWindowsMessage varMessage = null;
+        if (obj.equals("this")) {
+            if (currentMessage == null) {
+                throw new IllegalArgumentException("Failure obtaining term value for rule " +
+                    currentRuleName +
+                    ": \"this\" is not a valid name for generating runtime messages.");
+            }
+            varMessage = new ReplayWindowsMessage(currentMessage);
+        }
+        else {
+            Object tmp = messageStorage.get(obj);
+            if (tmp instanceof ReplayWindowsMessage) {
+                varMessage = (ReplayWindowsMessage) tmp;
+            }
+            else {
+                throw new IllegalArgumentException("Failure obtaining term value for rule " +
+                    currentRuleName + ": No message \"" + obj + "\" stored.");
+            }
+        }
+        return varMessage;
+    }
+
+    /**
+     * <p>
+     * Retrieves a stored message sequence from the storage.
+     * </p>
+     * 
+     * @param obj
+     *            object identifier in the storage
+     * @return message sequence retrieved from the storage
+     * @throws IllegalArgumentException
+     *             thrown if no message sequences with the identifier obj is found in the storage
+     */
+    @SuppressWarnings("unchecked")
+    private List<ReplayWindowsMessage> getStoredSeqVariable(String obj)
+        throws IllegalArgumentException
+    {
+        List<ReplayWindowsMessage> varMsgSeq = null;
+        Object tmp = messageStorage.get(obj);
+        if (tmp instanceof List<?>) {
+            varMsgSeq = (List<ReplayWindowsMessage>) tmp;
+        }
+        else {
+            throw new IllegalArgumentException("Failure obtaining term value for rule " +
+                                               currentRuleName + ": No sequence \"" + obj +
+                                               "\" store.");
+        }
+        return varMsgSeq;
+    }
+
+    /**
+     * <p>
+     * convenience method for {@link #getTermValue(WindowsMessage, Term)} with current message is
+     * null.
+     * </p>
+     * 
+     * @param termElement
+     *            {@link Element} representing the term node
+     * @return value of the term or {@code null} of the term node could not be evaluated
+     */
+    private <T> T getTermValue(EventGenerationRule.Term term, Class<T> expectedType) {
+        return getTermValue(null, term, expectedType);
+    }
+
+    /**
+     * <p>
+     * Handles term-nodes and returns the value of the described term.
+     * </p>
+     * 
+     * @param currentMessage
+     *            current message during the parsing; required to resolve references to "this" in a
+     *            term
+     * @param termElement
+     *            {@link Element} representing the term node
+     * @return value of the term or {@code null} of the term node could not be evaluated
+     */
+    private <T> T getTermValue(WindowsMessage           currentMessage,
+                               EventGenerationRule.Term term,
+                               Class<T>                 expectedType)
+    {
+        T value = null;
+        
+        if ("constValue".equals(term.getName())) {
+            value = getValueAsType(term.getValue(), expectedType);
+        }
+        else if ("paramValue".equals(term.getName())) {
+            String objectName = term.getMessageId();
+            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
+            if (varMessage != null) {
+                String param = term.getMessageParameterName();
+                value = getValueAsType(varMessage.getParameter(param), expectedType);
+            }
+        }
+        else if ("winInfoValue".equals(term.getName())) {
+            String objectName = term.getMessageId();
+            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
+            if (varMessage != null) {
+                String paramString = term.getWindowParameterName();
+                if (paramString.equals("class")) {
+                    value = getValueAsType
+                        (((MFCGUIElement) varMessage.getTarget()).getType(), expectedType);
+                }
+                else if (paramString.equals("resourceId")) {
+                    value = getValueAsType
+                        (((MFCGUIElement) varMessage.getTarget()).getResourceId(), expectedType);
+                }
+                else if (paramString.equals("hwnd")) {
+                    value = getValueAsType
+                        (((MFCGUIElement) varMessage.getTarget()).getId(), expectedType);
+                }
+                else if (paramString.equals("parentTarget")) {
+                    String target = varMessage.getTargetXML();
+                    int index = target.lastIndexOf("<");
+                    if (index == 0) {
+                        Console.traceln(Level.WARNING, "Trying to adress parent of top-level " +
+                                        "window! Replay probably invalid!");
+                    }
+                    value =  getValueAsType(target.substring(0, index), expectedType);
+                }
+                else if (paramString.equals("parentClass")) {
+                    value =  getValueAsType
+                        (((MFCGUIElement) varMessage.getTarget())
+                        .getParent().getSpecification().getType(), expectedType);
+                }
+            }
+        }
+        else if ("msgInfoValue".equals(term.getName())) {
+            String objectName = term.getMessageId();
+            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
+            if (varMessage != null) {
+                String paramString = term.getMessageInfoName();
+                if (paramString.equals("type")) {
+                    value = getValueAsType(varMessage.getType(), expectedType);
+                }
+                else if (paramString.equals("target")) {
+                    value = getValueAsType(varMessage.getTargetXML(), expectedType);
+                }
+            }
+        }
+        else if ("msgInfoValue".equals(term.getName())) {
+            String objectName = term.getMessageId();
+            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
+            if (varMessage != null) {
+                String paramString = term.getMessageInfoName();
+                if (paramString.equals("type")) {
+                    value = getValueAsType(varMessage.getType(), expectedType);
+                }
+                else if (paramString.equals("target")) {
+                    value = getValueAsType(varMessage.getTargetXML(), expectedType);
+                }
+            }
+        }
+        
+        return value;
+    }
+    
+    /**
+     * <p>
+     * Convenience method for {@link #getTermValueAsList(WindowsMessage, Term)} with current
+     * message is null.
+     * </p>
+     * 
+     * @param termElement
+     *            {@link Element} representing the term node
+     * @return value of the term or {@code null} of the term node could not be evaluated
+     */
+    private <T> List<T> getTermValueAsList(EventGenerationRule.Term term, Class<T> expectedType) {
+        return getTermValueAsList(null, term, expectedType);
+    }
+
+    /**
+     * <p>
+     * Handles term-nodes and returns the value of the described term.
+     * </p>
+     * 
+     * @param currentMessage
+     *            current message during the parsing; required to resolve references to "this" in a
+     *            term
+     * @param termElement
+     *            {@link Element} representing the term node
+     * @return value of the term or {@code null} of the term node could not be evaluated
+     */
+    private <T> List<T> getTermValueAsList(WindowsMessage           currentMessage,
+                                           EventGenerationRule.Term term,
+                                           Class<T>                 expectedElementType)
+    {
+        List<T> values = new ArrayList<T>();
+        if ("seqValue".equals(term.getName())) {
+            List<ReplayWindowsMessage> seqVar = getStoredSeqVariable(term.getSequenceId());
+            Object value;
+            
+            for (ReplayWindowsMessage msg : seqVar) {
+                // msg.getParameter returns null, if parameter is not found,
+                // therefore the List can contain null-values
+                value = msg.getParameter(term.getSequenceParameterName());
+                values.add(getValueAsType(value, expectedElementType));
+            }
+        }
+        else {
+            values.add(getTermValue(currentMessage, term, expectedElementType));
+        }
+        
+        return values;
+    }
+    
+    /**
+     * <p>
+     * Resolves term values.
+     * </p>
+     *
+     * @param value value to be resolved
+     * @param expectedType class defining the expected type
+     * @return resolved value
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T getValueAsType(Object value, Class<T> expectedType) {
+        if (expectedType.isInstance(value)) {
+            return (T) value;
+        }
+        else if (value instanceof String) {
+            try {
+                if (WindowsMessageType.class.equals(expectedType)) {
+                    return (T) WindowsMessageType.parseMessageType((String) value);
+                }
+                else if (Short.class.equals(expectedType)) {
+                    return (T) (Short) Short.parseShort((String) value);
+                }
+                else if (Long.class.equals(expectedType)) {
+                    return (T) (Long) Long.parseLong((String) value);
+                }
+            }
+            catch (Exception e) {
+                // in this case, the value can not be transformed to the expected value. So ignore
+                // the exception and fall through to the exception thrown anyway
+            }
+        }
+        else if (value instanceof Long) {
+            try {
+                if (Short.class.equals(expectedType)) {
+                    return (T) (Short) ((Long) value).shortValue();
+                }
+                else if (String.class.equals(expectedType)) {
+                    return (T) ((Long) value).toString();
+                }
+            }
+            catch (Exception e) {
+                // in this case, the value can not be transformed to the expected value. So ignore
+                // the exception and fall through to the exception thrown anyway
+            }
+        }
+        
+        throw new IllegalArgumentException("the term value is not of the expected type " +
+                                           expectedType + " but a " +
+                                           (value != null ? value.getClass() : "null"));
+    }
+
+    /**
+     * <p>
+     * Handles LOWORD and HIWORD child nodes of LPARAM and WPARAM nodes. The returned value is the
+     * LPARAM/WPARAM value based on the LOWORD and HIWORD.
+     * </p>
+     * 
+     * @param param
+     *            {@link Element} representing the LPARAM/WPARAM node
+     * @return value of the LPARAM/WPARAM
+     */
+    private long loHiWord(EventGenerationRule.Term lword, EventGenerationRule.Term hword) {
+        return MAKEPARAM(getTermValue(lword, Short.class), getTermValue(hword, Short.class));
+    }
+
+    /**
+     * <p>
+     * Takes to short integers and combines them into the high and low order bits of an integer.
+     * </p>
+     * 
+     * @param loword
+     *            low word
+     * @param hiword
+     *            high word
+     * @return combined integer
+     */
+    private static int MAKEPARAM(short loword, short hiword) {
+        return loword | ((int) hiword) << Short.SIZE;
+    }
+
+    /**
+     * <p>
+     * Parses the rules.
+     * </p>
+     *
+     */
+    @SuppressWarnings("unchecked")
+    private void parseGenerationRules() {
+        SAXBuilder builder = new SAXBuilder();
+        Document doc = null;
+
+        try {
+            doc = builder.build(rulesFile);
+            rulesNamespace = Namespace.getNamespace("ul:rules");
+        }
+        catch (JDOMException e) {
+            Console.printerrln("Invalid rules file.");
+            Console.logException(e);
+            return;
+        }
+        catch (IOException e) {
+            Console.printerrln("Invalid rules file.");
+            Console.logException(e);
+            return;
+        }
+
+        Element rulesRoot = doc.getRootElement();
+        
+        List<Element> ruleElements = rulesRoot.getChildren("rule", rulesNamespace);
+
+        generationRules = new ArrayList<EventGenerationRule>();
+
+        for (Element ruleElement : ruleElements) {
+            generationRules.add(new EventGenerationRule(ruleElement, rulesNamespace));
+        }
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerCreate.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerCreate.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerCreate.java	(revision 922)
@@ -0,0 +1,131 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
+
+/**
+ * <p>
+ * Message handler for {@code WM_CREATE} messages. The handler maintains the {@link WindowTree}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class HandlerCreate extends MessageHandler {
+
+    /**
+     * <p>
+     * Constructor. Creates a new HandlerCreate.
+     * </p>
+     * 
+     * @param windowTree the tree of GUI element specifications to be created and adapted during
+     *                   parsing
+     */
+    public HandlerCreate(WindowTree windowTree) {
+        super(windowTree);
+    }
+
+    /**
+     * <p>
+     * Name of the created window.
+     * </p>
+     */
+    private String windowName;
+
+    /**
+     * <p>
+     * HWND of the created window.
+     * </p>
+     */
+    private long hwnd;
+
+    /**
+     * <p>
+     * HWND of the created window's parent.
+     * </p>
+     */
+    private long parentHwnd;
+
+    /**
+     * <p>
+     * Resource Id of the created window.
+     * </p>
+     */
+    private int resourceId;
+
+    /**
+     * <p>
+     * Window class of the created window.
+     * </p>
+     */
+    private String className;
+
+    /**
+     * <p>
+     * Modality of the created window.
+     * </p>
+     */
+    private boolean isModal;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onEndElement()
+     */
+    @Override
+    public void onEndElement() {
+        if (hwnd != 0) {
+            super.getWindowTree().add(parentHwnd, hwnd, windowName, resourceId, className, isModal);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onParameter(java.lang.String ,
+     * java.lang.String)
+     */
+    @Override
+    public void onParameter(String name, String value) {
+        if (name.equals("window.hwnd")) {
+            hwnd = Long.parseLong(value);
+        }
+        else if (name.equals("window.name")) {
+            windowName = value;
+        }
+        else if (name.equals("window.parent.hwnd")) {
+            parentHwnd = Long.parseLong(value);
+        }
+        else if (name.equals("window.resourceId")) {
+            resourceId = Integer.parseInt(value);
+        }
+        else if (name.equals("window.class")) {
+            if (value.startsWith("Afx:")) {
+                className = "Afx:";
+            }
+            else {
+                className = value;
+            }
+        }
+        else if (name.equals("window.ismodal")) {
+            if (value.equals("true") || value.equals("1")) {
+                isModal = true;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onStartElement()
+     */
+    @Override
+    public void onStartElement() {
+        windowName = "";
+        hwnd = 0;
+        parentHwnd = 0;
+        resourceId = 0;
+        className = "";
+        isModal = false;
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerDestroy.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerDestroy.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerDestroy.java	(revision 922)
@@ -0,0 +1,69 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
+
+/**
+ * <p>
+ * Handler for {@code WM_DESTROY} message. The handler maintains the {@link WindowTree}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class HandlerDestroy extends MessageHandler {
+
+    /**
+     * <p>
+     * Constructor. Creates a new HandlerDestroy.
+     * </p>
+     * 
+     * @param windowTree
+     *            the tree of GUI element specifications to be created and adapted during parsing
+     */
+    public HandlerDestroy(WindowTree windowTree) {
+        super(windowTree);
+    }
+
+    /**
+     * <p>
+     * HWND of the window that is destroyed.
+     * </p>
+     */
+    private long hwnd;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onEndElement()
+     */
+    @Override
+    public void onEndElement() {
+        if (hwnd != 0) {
+            super.getWindowTree().remove(hwnd);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onParameter(java.lang.String ,
+     * java.lang.String)
+     */
+    @Override
+    public void onParameter(String name, String value) {
+        if (name.equals("window.hwnd")) {
+            hwnd = Long.parseLong(value);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onStartElement()
+     */
+    @Override
+    public void onStartElement() {
+        hwnd = 0;
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerSetText.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerSetText.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/HandlerSetText.java	(revision 922)
@@ -0,0 +1,79 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
+
+/**
+ * <p>
+ * Handles {@code WM_SETTEXT} messages. Handler maintains the {@link WindowTree}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class HandlerSetText extends MessageHandler {
+
+    /**
+     * <p>
+     * Constructor. Creates a new HanderSetText.
+     * </p>
+     * 
+     * @param windowTree
+     *            the tree of GUI element specifications to be created and adapted during parsing
+     */
+    public HandlerSetText(WindowTree windowTree) {
+        super(windowTree);
+    }
+
+    /**
+     * <p>
+     * New name of the window.
+     * </p>
+     */
+    private String windowName;
+
+    /**
+     * <p>
+     * HWND of the window.
+     * </p>
+     */
+    private long hwnd;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onEndElement()
+     */
+    @Override
+    public void onEndElement() {
+        if (hwnd != 0) {
+            super.getWindowTree().setName(hwnd, windowName);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onParameter(java.lang.String ,
+     * java.lang.String)
+     */
+    @Override
+    public void onParameter(String name, String value) {
+        if (name.equals("window.hwnd")) {
+            hwnd = Long.parseLong(value);
+        }
+        else if (name.equals("window.newText")) {
+            windowName = value;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.mfc.MessageHandler#onStartElement()
+     */
+    @Override
+    public void onStartElement() {
+        windowName = "";
+        hwnd = 0;
+    }
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/LogPreprocessor.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/LogPreprocessor.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/LogPreprocessor.java	(revision 922)
@@ -0,0 +1,221 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.logging.Level;
+
+import org.apache.commons.codec.binary.Base64;
+
+import de.ugoe.cs.util.FileTools;
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Pre-processes log files generated by the EventBench's MFCUsageMonitor. It
+ * decodes Base64 encoding into UTF-16. It removes all lines of the log file,
+ * that do not start with the prefix "UL:", end everything before the prefix and
+ * the prefix itself.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class LogPreprocessor {
+
+	/**
+	 * <p>
+	 * Internal flag that monitors whether there is an open session-node in the
+	 * XML file to ensure that there is a closing session-node for each opening
+	 * session node and, thereby, ensure that the XML file is well formed.
+	 * </p>
+	 */
+	private boolean sessionOpen = false;
+
+	/**
+	 * <p>
+	 * Internal flag that monitors whether a message node is longer than one
+	 * line, as the prefix handling is different in this case.
+	 * </p>
+	 */
+	private boolean msgIncomplete = false;
+
+	/**
+	 * <p>
+	 * Flag that marks whether the log file is Base64 encoded.
+	 * </p>
+	 */
+	private boolean base64;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new LogPreprocessor that does not decode Base64.
+	 * </p>
+	 */
+	public LogPreprocessor() {
+		this(false);
+	}
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new LogPreprocessor.
+	 * </p>
+	 * 
+	 * @param base64
+	 *            if true, Base64 will be decoded.
+	 */
+	public LogPreprocessor(boolean base64) {
+		this.base64 = base64;
+	}
+
+	/**
+	 * <p>
+	 * Pre-processes a single log file.
+	 * </p>
+	 * 
+	 * @param source
+	 *            name and path of the source file
+	 * @param target
+	 *            name and path of the target file
+	 * @throws IOException
+	 *             thrown if there is a problem with reading from or writing to
+	 *             the source, respectively target file
+	 * @throws FileNotFoundException
+	 *             thrown if the source file is not found
+	 */
+	public void convertToXml(String source, String target) throws IOException,
+			FileNotFoundException {
+		OutputStreamWriter targetFile = new OutputStreamWriter(
+				new FileOutputStream(target), "UTF-8");
+		targetFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+				+ StringTools.ENDLINE);
+		targetFile.write("<log>" + StringTools.ENDLINE);
+		processFile(source, targetFile);
+		if (sessionOpen) {
+			targetFile.write(" </session>" + StringTools.ENDLINE);
+		}
+		targetFile.write("</log>");
+		targetFile.close();
+	}
+
+	/**
+	 * <p>
+	 * Pre-processes all files in a given source folder.
+	 * </p>
+	 * 
+	 * @param path
+	 *            path of the source folder
+	 * @param target
+	 *            name and path of the target file
+	 * @throws IOException
+	 *             thrown if there is a problem with reading from or writing to
+	 *             the source, respectively target file
+	 * @throws FileNotFoundException
+	 *             thrown if the source file is not found
+	 */
+	public void convertDirToXml(String path, String target) throws IOException,
+			FileNotFoundException {
+		OutputStreamWriter targetFile = new OutputStreamWriter(
+				new FileOutputStream(target), "UTF-8");
+		targetFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+				+ StringTools.ENDLINE);
+		targetFile.write("<log>" + StringTools.ENDLINE);
+		File folder = new File(path);
+		if (!folder.isDirectory()) {
+		    targetFile.close();
+			throw new IOException(path + " is not a directory");
+		}
+		String absolutPath = folder.getAbsolutePath();
+		for (String filename : folder.list()) {
+			String source = absolutPath + "/" + filename;
+			Console.traceln(Level.INFO, "Processing file: " + source);
+			processFile(source, targetFile);
+		}
+
+		if (sessionOpen) {
+			targetFile.write(" </session>" + StringTools.ENDLINE);
+		}
+		targetFile.write("</log>");
+		targetFile.close();
+	}
+
+	/**
+	 * <p>
+	 * Internal function that pre-processes a log file.
+	 * </p>
+	 * 
+	 * @param source
+	 *            name and path of the source file
+	 * @param target
+	 *            name and path of the target file
+	 * @throws IOException
+	 *             thrown if there is a problem with reading from or writing to
+	 *             the source, respectively target file
+	 * @throws FileNotFoundException
+	 *             thrown if the source file is not found
+	 */
+	private void processFile(String source, OutputStreamWriter targetFile)
+			throws FileNotFoundException, IOException {
+		String[] lines = FileTools.getLinesFromFile(source, false);
+		String incompleteLine = "";
+		for (String currentLine : lines) {
+			if (currentLine.contains("UL: <session>")) {
+				if (sessionOpen) {
+					targetFile.write(" </session>" + StringTools.ENDLINE);
+					targetFile.write(" <session>" + StringTools.ENDLINE);
+				} else {
+					targetFile.write(" <session>" + StringTools.ENDLINE);
+					sessionOpen = true;
+				}
+			} else if (currentLine.contains("UL: </session>")) {
+				if (sessionOpen) {
+					targetFile.write(" </session>" + StringTools.ENDLINE);
+					sessionOpen = false;
+				}
+			} else if (msgIncomplete || currentLine.contains("UL: ")) {
+
+				String currentContent;
+				String actualLine;
+				if (msgIncomplete) {
+					actualLine = currentLine;
+				} else {
+					String[] splitResult = currentLine.split("UL: ");
+					actualLine = splitResult[1];
+				}
+				if (base64) {
+					Base64 decoder = new Base64();
+					byte[] decoded = decoder.decode(actualLine);
+					currentContent = new String(decoded, "UTF-16LE");
+					if( currentContent.length()!=0 ) {
+						currentContent = currentContent.substring(0,
+								currentContent.length() - 1);
+					}
+				} else {
+					currentContent = actualLine;
+				}
+				if (msgIncomplete) {
+					incompleteLine += currentContent;
+					if (incompleteLine.contains("</msg>")) {
+						msgIncomplete = false;
+						targetFile.write(incompleteLine + StringTools.ENDLINE);
+						incompleteLine = "";
+					}
+				} else {
+					if (currentContent.contains("<msg") && sessionOpen) {
+						if (currentContent.contains("</msg>")) {
+							targetFile.write("  " + currentContent
+									+ StringTools.ENDLINE);
+						} else {
+							msgIncomplete = true;
+							incompleteLine += currentContent;
+						}
+					}
+				}
+			}
+		}
+	}
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCLogParser.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCLogParser.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCLogParser.java	(revision 922)
@@ -0,0 +1,354 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+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.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.logging.Level;
+
+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.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessage;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessageType;
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCGUIElement;
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * This class provides functionality to parse XML log files generated by the MFCUsageMonitor of
+ * EventBench. The result of parsing a file is a collection of event sequences. It uses the
+ * {@link SequenceSplitter} and the {@link EventGenerator} as well as custom defined
+ * {@link MessageHandler} for the parsing.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class MFCLogParser extends DefaultHandler {
+
+    /**
+     * <p>
+     * If a custom message handler is used, this field contains its handle. Otherwise this field is
+     * {@code null}.
+     * </p>
+     */
+    private MessageHandler currentHandler;
+
+    /**
+     * <p>
+     * internal handle to the current window tree
+     * </p>
+     */
+    private WindowTree currentWindowTree;
+
+    /**
+     * <p>
+     * the type of the currently parsed message
+     * </p>
+     */
+    private WindowsMessageType currentMessageType;
+    
+    /**
+     * <p>
+     * the parameters of the currently parsed message
+     * </p>
+     */
+    private Map<String, Object> currentMessageParameters = new HashMap<String, Object>();
+    
+    /**
+     * <p>
+     * {@link SequenceSplitter} instance used by the {@link MFCLogParser}.
+     * </p>
+     */
+    private SequenceSplitter sequenceSplitter;
+
+    /**
+     * <p>
+     * Collection of message sequences that is contained in the log file, which is parsed.
+     * </p>
+     */
+    private Collection<List<Event>> sequences;
+
+    /**
+     * <p>
+     * Debugging variable that allows the analysis which message type occurs how often in the log
+     * file. Can be used to enhance the message filter.
+     * </p>
+     */
+    private SortedMap<WindowsMessageType, Integer> typeCounter;
+
+    /**
+     * <p>
+     * Debugging variable that enables the counting of the occurrences of each message. Used in
+     * combination with {@link #typeCounter}.
+     * </p>
+     */
+    private boolean countMessageOccurences;
+
+    /**
+     * <p>
+     * Constructor. Creates a new LogParser that does not count message occurrences.
+     * </p>
+     */
+    public MFCLogParser() {
+        this(false);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new LogParser.
+     * </p>
+     * 
+     * @param countMessageOccurences
+     *            if true, the occurrences of each message type in the log is counted.
+     */
+    public MFCLogParser(boolean countMessageOccurences) {
+        sequences = new LinkedList<List<Event>>();
+        currentHandler = null;
+        this.countMessageOccurences = countMessageOccurences;
+        if (countMessageOccurences) {
+            typeCounter = new TreeMap<WindowsMessageType, Integer>();
+        }
+    }
+
+    /**
+     * <p>
+     * Parses a log file written by the MFCMonitor 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 MFCMonitor 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);
+        }
+        catch (ParserConfigurationException e) {
+            Console.printerr("Error parsing file " + file.getName());
+            Console.logException(e);
+        }
+        catch (SAXException e) {
+            Console.printerr("Error parsing file " + file.getName());
+            Console.logException(e);
+        }
+        catch (FileNotFoundException e) {
+            Console.printerr("Error parsing file " + file.getName());
+            Console.logException(e);
+        }
+        
+        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);
+            }
+            catch (SAXException e) {
+                Console.printerr("Error parsing file " + file.getName());
+                Console.logException(e);
+            }
+            catch (IOException e) {
+                Console.printerr("Error parsing file " + file.getName());
+                Console.logException(e);
+            }
+        }
+        
+        if (countMessageOccurences) {
+            Console.println("Message statistics:");
+            Console.println
+                (typeCounter.toString().replace(" ", StringTools.ENDLINE).replaceAll("[\\{\\}]", ""));
+        }
+    }
+    
+    /**
+     * <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 collection of event sequences
+     */
+    public GUIModel getGuiModel() {
+        if( currentWindowTree!=null ) {
+            return currentWindowTree.getGUIModel();
+        } else {
+            return null;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
+     * java.lang.String, org.xml.sax.Attributes)
+     */
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes atts)
+        throws SAXException
+    {
+        if (qName.equals("session")) {
+            Console.traceln(Level.FINE, "start of session");
+            // in some logs, the session end may be marked in between the log. This is because
+            // of thread problems. So instead of creating a new GUI model, preserve it.
+            if (currentWindowTree == null) {
+                currentWindowTree = new WindowTree();
+            }
+            sequenceSplitter = new SequenceSplitter(currentWindowTree);
+        }
+        else if (qName.equals("msg")) {
+            currentMessageType = WindowsMessageType.parseMessageType(atts.getValue("type"));
+
+            if (countMessageOccurences) {
+                Integer currentCount = typeCounter.get(currentMessageType);
+                if (currentCount == null) {
+                    typeCounter.put(currentMessageType, 1);
+                }
+                else {
+                    typeCounter.put(currentMessageType, currentCount + 1);
+                }
+            }
+
+            if (currentMessageType == WindowsMessageType.WM_CREATE) {
+                currentHandler = new HandlerCreate(currentWindowTree);
+                currentHandler.onStartElement();
+            }
+            else if (currentMessageType == WindowsMessageType.WM_DESTROY) {
+                currentHandler = new HandlerDestroy(currentWindowTree);
+                currentHandler.onStartElement();
+            }
+            else if (currentMessageType == WindowsMessageType.WM_SETTEXT) {
+                currentHandler = new HandlerSetText(currentWindowTree);
+                currentHandler.onStartElement();
+            }
+        }
+        else if (qName.equals("param")) {
+            if (currentHandler != null) {
+                currentHandler.onParameter(atts.getValue("name"), atts.getValue("value"));
+            }
+            else {
+                // provide the parameters directly in the correct type
+                String paramName = atts.getValue("name");
+                if (("window.hwnd".equals(paramName)) ||
+                    ("source".equals(paramName)) ||
+                    ("LPARAM".equals(paramName)) ||
+                    ("WPARAM".equals(paramName)) ||
+                    ("scrollPos".equals(paramName)) ||
+                    ("scrollBarHandle".equals(paramName)))
+                {
+                    Long paramValue = Long.parseLong(atts.getValue("value"));
+                    currentMessageParameters.put(paramName, paramValue);
+                }
+                else {
+                    currentMessageParameters.put(paramName, atts.getValue("value"));
+                }
+            }
+        }
+    }
+
+    /*
+     * (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("msg")) {
+            if (currentHandler != null) {
+                currentHandler.onEndElement();
+                currentHandler = null;
+            }
+            else {
+                try {
+                    long hwnd = (Long) currentMessageParameters.get("window.hwnd");
+                    MFCGUIElement target = currentWindowTree.find(hwnd);
+                    
+                    WindowsMessage message = new WindowsMessage
+                        (currentMessageType, target, currentMessageParameters);
+                    
+                    sequenceSplitter.addMessage(message);
+                }
+                catch (IllegalArgumentException e) {
+                    Console.traceln(Level.WARNING, e.getMessage() + " WindowsMessage " + currentMessageType +
+                                    " ignored.");
+                }
+            }
+        }
+        else if (qName.equals("session")) {
+            sequenceSplitter.endSession();
+            List<Event> seq = sequenceSplitter.getSequence();
+            if (seq != null && !seq.isEmpty()) {
+                sequences.add(seq);
+            }
+            Console.traceln(Level.FINE, "end of session");
+        }
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCPlugin.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCPlugin.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCPlugin.java	(revision 922)
@@ -0,0 +1,46 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.plugin.QuestPlugin;
+
+/**
+ * <p>
+ * Identifier class for the QUEST MFC plug-in.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class MFCPlugin implements QuestPlugin {
+
+	/**
+	 * <p>
+	 * The command packages of this plug-in.
+	 * </p>
+	 */
+	private final static String[] commandPackages = new String[] { "de.ugoe.cs.autoquest.plugin.mfc.commands" };
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.plugin.QuestPlugin#getTitle()
+	 */
+	@Override
+	public String getTitle() {
+		return "MFC-Plugin";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.plugin.QuestPlugin#getCommandPackages()
+	 */
+	@Override
+	public List<String> getCommandPackages() {
+	        return Collections.unmodifiableList(Arrays.asList(commandPackages));
+	}
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCReplayDecorator.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCReplayDecorator.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCReplayDecorator.java	(revision 922)
@@ -0,0 +1,96 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * {@link IReplayDecorator} for replay generated for EventBench's MFCReplay tool.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class MFCReplayDecorator implements IReplayDecorator {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * The instance of the {@link MFCReplayDecorator} (implemented as
+	 * singleton).
+	 * </p>
+	 */
+	transient private static MFCReplayDecorator theInstance;
+
+	/**
+	 * <p>
+	 * Constructor. Private to guarantee that only one instance of the replay
+	 * generator exists.
+	 * </p>
+	 */
+	private MFCReplayDecorator() {
+	};
+
+	/**
+	 * <p>
+	 * Returns the instance of the MFCReplayDecorator.
+	 * </p>
+	 * 
+	 * @return instance of the MFCReplayDecorator.
+	 */
+	public static MFCReplayDecorator getInstance() {
+		if (theInstance == null) {
+			theInstance = new MFCReplayDecorator();
+		}
+		return theInstance;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.IReplayDecorator#getHeader()
+	 */
+	@Override
+	public String getHeader() {
+		return "<?xml version=\"1.0\" encoding=\"UTF-16\"?>"
+				+ StringTools.ENDLINE + "<log>" + StringTools.ENDLINE;
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.IReplayDecorator#getFooter()
+	 */
+	@Override
+	public String getFooter() {
+		return "</log>" + StringTools.ENDLINE;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.IReplayDecorator#getSessionHeader(int)
+	 */
+	@Override
+	public String getSessionHeader(int sessionId) {
+		return " <session id=\"" + sessionId + "\">" + StringTools.ENDLINE;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.IReplayDecorator#getSessionFooter(int)
+	 */
+	@Override
+	public String getSessionFooter(int sessionId) {
+		return " </session>" + StringTools.ENDLINE;
+	}
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MessageHandler.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MessageHandler.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MessageHandler.java	(revision 922)
@@ -0,0 +1,70 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
+
+/**
+ * <p>
+ * Base class to define custom message handlers, for messages that shall be handled differently
+ * during the parsing of usage logs. It provides dummy implementations for all required methods,
+ * such that implementations can only overwrite the parts they actually require and ignore the rest.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class MessageHandler {
+
+    /**
+     * <p>
+     * during parsing, a tree of GUI elements is created and adapted.
+     * This is the reference to it.
+     * </p>
+     */
+    private WindowTree windowTree;
+
+    /**
+     * <p>
+     * Constructor. Protected to prohibit initialization of the base class itself.
+     * </p>
+     * 
+     * @param windowTree the tree of GUI element specifications to be created and adapted during
+     *                   parsing
+     */
+    protected MessageHandler(WindowTree windowTree) {
+        this.windowTree = windowTree;
+    }
+
+    /**
+     * <p>
+     * Called in the startElement() method of the {@link MFCLogParser} when a msg-node begins.
+     * </p>
+     */
+    public void onStartElement() {}
+
+    /**
+     * <p>
+     * Called by the {@link MFCLogParser} to handle param-nodes.
+     * </p>
+     * 
+     * @param name
+     *            name (type) of the parameter
+     * @param value
+     *            value of the parameter
+     */
+    public void onParameter(String name, String value) {}
+
+    /**
+     * <p>
+     * Called in the endElement() method of {@link MFCLogParser} when a msg-node ends.
+     * </p>
+     */
+    public void onEndElement() {}
+
+    /**
+     * @return the window tree created and adapted during parsing
+     */
+    protected WindowTree getWindowTree() {
+        return windowTree;
+    }
+    
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/SequenceSplitter.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/SequenceSplitter.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/SequenceSplitter.java	(revision 922)
@@ -0,0 +1,175 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessage;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessageType;
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Responsible to split sequences into subsequences, such that each subsequences contains exactly
+ * one event.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class SequenceSplitter {
+
+    /**
+     * <p>
+     * Contains the current subsequence.
+     * </p>
+     */
+    private List<WindowsMessage> currentSequence;
+
+    /**
+     * <p>
+     * Number of messages in the current sequences, that signal that a key or mouse button has been
+     * pressed down to which not yet a message has been found, that signals that the button has been
+     * released.
+     * </p>
+     */
+    private int openDowns;
+
+    /**
+     * <p>
+     * Internal flag that signals if {@link #currentSequence} needs to be initialized.
+     * </p>
+     */
+    private boolean initMessages;
+
+    /**
+     * <p>
+     * The {@link EventGenerator} used to convert the subsequences into {@link Event}s
+     * </p>
+     */
+    private EventGenerator tokenGenerator;
+
+    /**
+     * <p>
+     * The event sequence generated.
+     * </p>
+     */
+    private List<Event> actionSequence;
+
+    /**
+     * <p>
+     * Type of the previous message.
+     * </p>
+     */
+    private WindowsMessageType prevMsg;
+
+    /**
+     * <p>
+     * Constructor. Creates a new SequenceSplitter.
+     * </p>
+     */
+    public SequenceSplitter(WindowTree windowTree) {
+        currentSequence = new LinkedList<WindowsMessage>();
+        openDowns = 0;
+        initMessages = true;
+        tokenGenerator = new EventGenerator(windowTree);
+        actionSequence = new LinkedList<Event>();
+        prevMsg = null;
+    }
+
+    /**
+     * <p>
+     * Called by the {@link MFCLogParser} every time a message is parsed.
+     * </p>
+     * 
+     * @param msg
+     *            message to be added
+     */
+    public void addMessage(WindowsMessage msg) {
+        if (startOfSequence(msg)) {
+            if (!initMessages) {
+                Event currentAction = tokenGenerator.generateEvent(currentSequence);
+                if (currentAction != null) {
+                    actionSequence.add(currentAction);
+                }
+                if (msg.getType().isKeyMessage() && openDowns > 0) {
+                    Console.traceln(Level.SEVERE, "Key message found with open down mouse " +
+                                    "messages - will probabably result in a faulty sequence.");
+                }
+            }
+            else {
+                initMessages = false;
+            }
+            currentSequence = new LinkedList<WindowsMessage>();
+        }
+        if (msg.getType().isUpMessage()) {
+            if (openDowns > 0) {
+                openDowns--;
+            }
+        }
+
+        // this fix checks if there are two consecutive mouse-down messages.
+        // This sometimes occurs due to incorrect filtering in the monitoring
+        // dll.
+        if (!(prevMsg == WindowsMessageType.WM_LBUTTONDOWN && prevMsg == msg.getType())) {
+            currentSequence.add(msg);
+        }
+        else {
+            openDowns--;
+        }
+        prevMsg = msg.getType();
+    }
+
+    /**
+     * <p>
+     * Returns the event sequence generated from the message that have been added.
+     * </p>
+     * 
+     * @return generated event sequence
+     */
+    public List<Event> getSequence() {
+        return actionSequence;
+    }
+
+    /**
+     * <p>
+     * Called when a session in the log file is finished, i.e., a closing session-node is found.
+     * </p>
+     */
+    public void endSession() {
+        Event currentAction = tokenGenerator.generateEvent(currentSequence);
+        if (currentAction != null) {
+            actionSequence.add(currentAction);
+        }
+    }
+
+    /**
+     * <p>
+     * Checks if the message starts a new subsequence and returns the result.
+     * </p>
+     * 
+     * @param msg
+     *            message that is checked
+     * @return true, if a new subsequence begins
+     */
+    private boolean startOfSequence(WindowsMessage msg) {
+        boolean isStart = false;
+        WindowsMessageType msgType = msg.getType();
+        if (msgType.isKeyMessage()) {
+            isStart = true;
+        }
+        if (msgType.isDownMessage()) {
+            openDowns++;
+            if (openDowns == 1) {
+                isStart = true;
+            }
+        }
+        if (msgType.isDblclkMessage()) {
+            openDowns++;
+        }
+        return isStart;
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDconvertDirToXml.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDconvertDirToXml.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDconvertDirToXml.java	(revision 922)
@@ -0,0 +1,58 @@
+package de.ugoe.cs.autoquest.plugin.mfc.commands;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.plugin.mfc.LogPreprocessor;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command to pre-process all files in a folder.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDconvertDirToXml implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "convertDirToXml <sourceDirectory> <targetFile> {<base64>}";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		if (parameters.size() < 2) {
+			throw new IllegalArgumentException();
+		}
+		String path = (String) parameters.get(0);
+		String target = (String) parameters.get(1);
+		boolean base64 = false;
+		if (parameters.size() == 3) {
+			base64 = Boolean.parseBoolean((String) parameters.get(2));
+		}
+
+		try {
+			new LogPreprocessor(base64).convertDirToXml(path, target);
+		} catch (FileNotFoundException e) {
+			Console.printerrln(e.getMessage());
+		} catch (IOException e) {
+			Console.printerrln(e.getMessage());
+		}
+
+	}
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDconvertToXml.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDconvertToXml.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDconvertToXml.java	(revision 922)
@@ -0,0 +1,58 @@
+package de.ugoe.cs.autoquest.plugin.mfc.commands;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.plugin.mfc.LogPreprocessor;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command to pre-process a single file.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDconvertToXml implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "convertToXml <sourceFile> <targetFile> {<base64>}";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		if (parameters.size() < 2) {
+			throw new IllegalArgumentException();
+		}
+		String source = (String) parameters.get(0);
+		String target = (String) parameters.get(1);
+		boolean base64 = false;
+		if (parameters.size() == 3) {
+			base64 = Boolean.parseBoolean((String) parameters.get(2));
+		}
+
+		try {
+			new LogPreprocessor(base64).convertToXml(source, target);
+		} catch (FileNotFoundException e) {
+			Console.printerrln(e.getMessage());
+		} catch (IOException e) {
+			Console.printerrln(e.getMessage());
+		}
+
+	}
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDparseXML.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDparseXML.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/commands/CMDparseXML.java	(revision 922)
@@ -0,0 +1,72 @@
+package de.ugoe.cs.autoquest.plugin.mfc.commands;
+
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.plugin.mfc.MFCLogParser;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to parse an XML file with sessions monitored by EventBench's MFCUsageMonitor.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDparseXML implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "parseXML <filename> {<sequencesName>} {<countMessageOccurences>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String filename;
+        String sequencesName = "sequences";
+        boolean countMessageOccurences = false;
+
+        try {
+            filename = (String) parameters.get(0);
+            if (parameters.size() >= 2) {
+                sequencesName = (String) parameters.get(1);
+            }
+            if (parameters.size() >= 3) {
+                countMessageOccurences = Boolean.parseBoolean((String) parameters.get(2));
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+
+        MFCLogParser parser = new MFCLogParser(countMessageOccurences);
+        parser.parseFile(filename);
+
+        Collection<List<Event>> sequences = parser.getSequences();
+
+        GUIModel targets = parser.getGuiModel();
+
+        if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+            CommandHelpers.dataOverwritten(sequencesName);
+        }
+        if (GlobalDataContainer.getInstance().addData(sequencesName + "_targets", targets)) {
+            CommandHelpers.dataOverwritten(sequencesName + "_targets");
+        }
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/MFCEventTypeFactory.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/MFCEventTypeFactory.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/MFCEventTypeFactory.java	(revision 922)
@@ -0,0 +1,199 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.eventcore;
+
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+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.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+
+/**
+ * <p>
+ * Creates the GUI event types (i.e., {@link IInteraction}s) for MFC events.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCEventTypeFactory {
+
+    /**
+     * <p>
+     * Instance of the singleton
+     * </p>
+     */
+    private static MFCEventTypeFactory instance = new MFCEventTypeFactory();
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCEventTypeFactory. Private to preserve singleton property.
+     * </p>
+     * 
+     */
+    private MFCEventTypeFactory() {}
+
+    /**
+     * <p>
+     * Returns the instance of the MFCEventTypeFactory.
+     * </p>
+     * 
+     * @return the instance
+     */
+    public static MFCEventTypeFactory getInstance() {
+        return instance;
+    }
+
+    /**
+     * <p>
+     * Returns the event type based on the name and parameters of a MFC event.
+     * </p>
+     * 
+     * @param eventName
+     *            name of the MFC event
+     * @param messageParameters
+     *            parameters of the MFC event
+     * @return the event type
+     */
+    public IEventType getEventType(String eventName, Map<String, String> messageParameters) {
+        if ("LeftClickButton".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("LeftClickListBox".equals(eventName)) {
+            return new ValueSelection<Integer>(getSelectedValue(messageParameters));
+        }
+        else if ("TabChange".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("LeftClickCommand".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("LeftClickSysCommand".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("NCLeftClickSysCommand".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("LeftClickMenuItemCmd".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("HScroll_TrackBar".equals(eventName)) {
+            return new ValueSelection<Integer>(getSelectedValue(messageParameters));
+        }
+        else if ("VScroll_TrackBar".equals(eventName)) {
+            return new ValueSelection<Integer>(getSelectedValue(messageParameters));
+        }
+        else if ("HScroll_ScrollBar".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("VScroll_ScrollBar".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("VScrollNC".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("LeftClickSetFocus".equals(eventName)) {
+            return new KeyboardFocusChange();
+        }
+        else if ("LeftClickChangeFocus".equals(eventName)) {
+            return new KeyboardFocusChange();
+        }
+        else if ("KeyDown".equals(eventName)) {
+            return new KeyPressed(getKey(messageParameters));
+        }
+        else if ("KeyUp".equals(eventName)) {
+            return new KeyReleased(getKey(messageParameters));
+        }
+        else if ("SysKeyDown".equals(eventName)) {
+            return new KeyPressed(getKey(messageParameters));
+        }
+        else if ("SysKeyUp".equals(eventName)) {
+            return new KeyReleased(getKey(messageParameters));
+        }
+        else if ("LeftClickCoordinates".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("NCLeftClickCoordinates".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("NCLeftClickCoordinates2".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("LeftClickCoordinatesTargetChanged".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else if ("LeftClickCoordinatesTargetChanged2".equals(eventName)) {
+            return new MouseClick(MouseButtonInteraction.Button.LEFT);
+        }
+        else {
+            throw new IllegalArgumentException("unknown event name: " + eventName);
+        }
+    }
+
+    /**
+     * <p>
+     * If the message parameters contain information about a key that has been pressed, the
+     * associated {@link VirtualKey} is returned.
+     * </p>
+     * 
+     * @param messageParameters
+     *            the message parameters
+     * @return key extracted from the message parameters
+     * @throws IllegalArgumentException
+     *             thrown if the messageParameters do not contain information about a key
+     */
+    private VirtualKey getKey(Map<String, String> messageParameters)
+        throws IllegalArgumentException
+    {
+        String value = null;
+
+        if (messageParameters != null) {
+            value = messageParameters.get("key");
+        }
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("no parameter \"key\" provided for key event. Please correct the event " +
+                 "generation rules");
+        }
+        
+        return WindowsVirtualKey.parseVirtualKey(value).getKey();
+    }
+
+    /**
+     * <p>
+     * If the message parameters contain information about a scroll position, the respective
+     * position is returned.
+     * </p>
+     * 
+     * @param messageParameters
+     *            the message parameters
+     * @return the scroll position
+     * @throws IllegalArgumentException
+     *             thrown if the messageParameters do not contain information about a scroll
+     *             position
+     */
+    private int getSelectedValue(Map<String, String> messageParameters)
+        throws IllegalArgumentException
+    {
+        String value = null;
+
+        if (messageParameters != null) {
+            value = messageParameters.get("scrollPos");
+        }
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("no parameter \"scrollPos\" provided for scroll event. Please correct the event " +
+                 "generation rules");
+        }
+        
+        return Integer.parseInt(value);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/ReplayWindowsMessage.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/ReplayWindowsMessage.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/ReplayWindowsMessage.java	(revision 922)
@@ -0,0 +1,278 @@
+package de.ugoe.cs.autoquest.plugin.mfc.eventcore;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.autoquest.eventcore.IReplayable;
+import de.ugoe.cs.autoquest.plugin.mfc.MFCReplayDecorator;
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * Contains all informations about a windows message, i.e., all parameters that are read when a
+ * windows message is parsed as well as its target, hwnd, etc.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ */
+public class ReplayWindowsMessage extends WindowsMessage implements IReplayable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * If the LPARAM contains a HWND, this string stores the target of the HWND.
+     * </p>
+     */
+    private String LPARAMasWindowDesc = null;
+
+    /**
+     * <p>
+     * If the WPARAM contains a HWND, this string stores the target of the HWND.
+     * </p>
+     */
+    private String WPARAMasWindowDesc = null;
+
+    /**
+     * <p>
+     * Delay after sending the messages during a replay. Default: 0
+     * </p>
+     */
+    private int delay = 0;
+
+    /**
+     * <p>
+     * Constructor. Creates a new replay message with a given {@link WindowsMessage}.
+     * </p>
+     * 
+     * @param replayedMessage
+     *            message from which the replay is generated
+     */
+    public ReplayWindowsMessage(WindowsMessage replayedMessage)
+    {
+        super(replayedMessage.getType(),
+              replayedMessage.getTarget(),
+              replayedMessage.getParameters());
+        
+        // the target may have changed in the meantime. So reset the targetXML to the one stored
+        // with the provided message
+        if (!super.getTargetXML().equals(replayedMessage.getTargetXML())) {
+            setTargetXML(replayedMessage.getTargetXML());
+        }
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new message with a given message type.
+     * </p>
+     * 
+     * @param type
+     *            type of the message
+     */
+    public ReplayWindowsMessage(WindowsMessageType type)
+    {
+        super(type);
+    }
+
+    /**
+     * <p>
+     * Two {@link ReplayWindowsMessage} are equal, if their {@link #type}, {@link #getTargetXML},
+     * and {@link #params} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        boolean isEqual = false;
+        if (other instanceof ReplayWindowsMessage) {
+            isEqual = super.equals(other);
+        }
+        return isEqual;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+    /**
+     * <p>
+     * Returns a string representation of the message of the form "msg[target=HWND;type=TYPE]".
+     * </p>
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "msg[target=" + getParameter("window.hwnd") + ";type=" + type + "]";
+    }
+
+    /**
+     * <p>
+     * Sets the LPARAM of a message.
+     * </p>
+     * 
+     * @param paramValue
+     *            value of the LPARAM
+     */
+    public void setLPARAM(long paramValue) {
+        super.addParameter("LPARAM", paramValue);
+    }
+
+    /**
+     * <p>
+     * Sets the WPARAM of a message.
+     * </p>
+     * 
+     * @param paramValue
+     *            value of the WPARAM
+     */
+    public void setWPARAM(long paramValue) {
+        super.addParameter("WPARAM", paramValue);
+    }
+
+    /**
+     * <p>
+     * If the LPARAM contains a HWND, this function can be used to set a target string to identify
+     * the HWND at run-time.
+     * </p>
+     * 
+     * @param windowDesc
+     *            target string
+     */
+    public void setLPARAMasWindowDesc(String windowDesc) {
+        LPARAMasWindowDesc = windowDesc;
+    }
+
+    /**
+     * <p>
+     * If the WPARAM contains a HWND, this function can be used to set a target string to identify
+     * the HWND at run-time.
+     * </p>
+     * 
+     * @param windowDesc
+     *            target string
+     */
+    public void setWPARAMasWindowDesc(String windowDesc) {
+        WPARAMasWindowDesc = windowDesc;
+    }
+
+    /**
+     * <p>
+     * If the LPARAM contains a HWND and the target string for the HWND is set, this function
+     * returns the target string. Otherwise, {@code null} is returned.
+     * </p>
+     * 
+     * @return target string if available; {@code null} otherwise
+     */
+    public String getLPARAMasWindowDesc() {
+        return LPARAMasWindowDesc;
+    }
+
+    /**
+     * <p>
+     * If the WPARAM contains a HWND and the target string for the HWND is set, this function
+     * returns the target string. Otherwise, {@code null} is returned.
+     * </p>
+     * 
+     * @return target string if available; {@code null} otherwise
+     */
+    public String getWPARAMasWindowDesc() {
+        return WPARAMasWindowDesc;
+    }
+
+    /**
+     * <p>
+     * Sets the XML target string.
+     * </p>
+     *
+     * @param targetXML the target string
+     */
+    public void setTargetXML(String targetXML) {
+        this.targetXML = targetXML;
+    }
+
+    /**
+     * <p>
+     * Returns the delay after this message during replays.
+     * </p>
+     * 
+     * @return delay after this message
+     */
+    public int getDelay() {
+        return delay;
+    }
+
+    /**
+     * <p>
+     * Sets the delay after this message during replays.
+     * </p>
+     * 
+     * @param delay
+     *            delay after this message
+     */
+    public void setDelay(int delay) {
+        this.delay = delay;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getReplay()
+     */
+    @Override
+    public String getReplay() {
+        StringBuilder currentMsgStr = new StringBuilder(400);
+        currentMsgStr.append("  <msg type=\"" + type.getNumber() + "\" ");
+        currentMsgStr.append("LPARAM=\"" + super.getLPARAM() + "\" ");
+        currentMsgStr.append("WPARAM=\"" + super.getWPARAM() + "\" ");
+        currentMsgStr.append("delay=\"" + delay + "\">");
+        if (LPARAMasWindowDesc != null) {
+            currentMsgStr.append(StringTools.ENDLINE);
+            currentMsgStr.append("   <LPARAM>");
+            currentMsgStr.append(StringTools.ENDLINE);
+            currentMsgStr.append(LPARAMasWindowDesc);
+            currentMsgStr.append(StringTools.ENDLINE);
+            currentMsgStr.append("</LPARAM>");
+        }
+        if (WPARAMasWindowDesc != null) {
+            currentMsgStr.append(StringTools.ENDLINE);
+            currentMsgStr.append("   <WPARAM>");
+            currentMsgStr.append(StringTools.ENDLINE);
+            currentMsgStr.append(WPARAMasWindowDesc);
+            currentMsgStr.append(StringTools.ENDLINE);
+            currentMsgStr.append("   </WPARAM>");
+        }
+        currentMsgStr.append(StringTools.ENDLINE);
+        currentMsgStr.append(super.getTargetXML());
+        currentMsgStr.append(StringTools.ENDLINE);
+        currentMsgStr.append("  </msg>");
+        currentMsgStr.append(StringTools.ENDLINE);
+        return currentMsgStr.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getDecorator()
+     */
+    @Override
+    public IReplayDecorator getDecorator() {
+        return MFCReplayDecorator.getInstance();
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsMessage.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsMessage.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsMessage.java	(revision 922)
@@ -0,0 +1,284 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.eventcore;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCGUIElement;
+
+/**
+ * <p>
+ * Contains all informations about a windows message, i.e., all parameters that are read when a
+ * windows message is parsed as well as its target, hwnd, etc.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ */
+public class WindowsMessage implements Serializable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Type of the message.
+     * </p>
+     */
+    final WindowsMessageType type;
+
+    /**
+     * <p>
+     * LPARAM of the message. Default: 0
+     * </p>
+     */
+    private long LPARAM = 0;
+
+    /**
+     * <p>
+     * WPARAM of the message. Default: 0
+     * </p>
+     */
+    private long WPARAM = 0;
+
+    /**
+     * <p>
+     * A map of all parameters, associated with the message, created during the parsing of messages
+     * from the logs {@code param}-nodes.
+     * </p>
+     */
+    private Map<String, Object> params = new HashMap<String, Object>();
+
+    /**
+     * <p>
+     * the target GUI element to which the message was sent
+     * </p>
+     */
+    private MFCGUIElement target;
+
+    /**
+     * <p>
+     * an XML representation of the target to preserve it as it was when this message was created
+     * </p>
+     */
+    protected String targetXML;
+
+    /**
+     * <p>
+     * Constructor. Creates a new message with a given message's type, target, and parameters.
+     * </p>
+     * 
+     * @param type
+     *            type of the message
+     * @param target
+     *            target of the message
+     * @param messageParameters
+     *            parameters of the message
+     */
+    public WindowsMessage(WindowsMessageType type,
+                          MFCGUIElement target,
+                          Map<String, Object> messageParameters)
+    {
+        this.type = type;
+        setTarget(target);
+
+        for (Map.Entry<String, Object> entry : messageParameters.entrySet()) {
+            addParameter(entry.getKey(), entry.getValue());
+        }
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new message with a given message type.
+     * </p>
+     * 
+     * @param type
+     *            type of the message
+     */
+    public WindowsMessage(WindowsMessageType type) {
+        this.type = type;
+    }
+
+    /**
+     * <p>
+     * Adds a parameter to the message.
+     * </p>
+     * 
+     * @param type
+     *            type descriptor of the parameter
+     * @param value
+     *            value of the parameter
+     */
+    public void addParameter(String type, Object value) {
+        params.put(type, value);
+        if (type.equals("LPARAM")) {
+            LPARAM = (Long) value;
+        }
+        else if (type.equals("WPARAM")) {
+            WPARAM = (Long) value;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the type of the message.
+     * </p>
+     * 
+     * @return type of the message
+     */
+    public WindowsMessageType getType() {
+        return type;
+    }
+
+    /**
+     * <p>
+     * Sets the message target.
+     * </p>
+     * 
+     * @param target
+     *            the target
+     */
+    public void setTarget(MFCGUIElement target) {
+        this.target = target;
+        this.targetXML = target.toXML();
+    }
+
+    /**
+     * <p>
+     * Returns the target of the message.
+     * </p>
+     * 
+     * @return the target
+     */
+    public MFCGUIElement getTarget() {
+        return target;
+    }
+
+    /**
+     * <p>
+     * Returns the value of a parameter, given its type. If the parameter is not found, {@code null}
+     * is returned.
+     * </p>
+     * 
+     * @param type
+     *            type of the parameter
+     * @return value of the parameter
+     */
+    public Object getParameter(String type) {
+        return params.get(type);
+    }
+
+    /**
+     * <p>
+     * Two {@link WindowsMessage} are equal, if their {@link #type}, {@link #getTargetXML},
+     * and {@link #params} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        boolean isEqual = false;
+        if (other instanceof WindowsMessage) {
+            isEqual =
+                ((WindowsMessage) other).type == this.type &&
+                    ((WindowsMessage) other).target.equals(this.target) &&
+                    ((WindowsMessage) other).params.equals(this.params);
+        }
+        return isEqual;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+
+        hash = multiplier * hash + type.hashCode();
+        hash = multiplier * hash + target.hashCode();
+        hash = multiplier * hash + params.hashCode();
+
+        return hash;
+    }
+
+    /**
+     * <p>
+     * Returns a string representation of the message of the form "msg[target=HWND;type=TYPE]".
+     * </p>
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "msg[target=" + getParameter("window.hwnd") + ";type=" + type + "]";
+    }
+
+    /**
+     * <p>
+     * Returns the LPARAM of a message.
+     * </p>
+     * 
+     * @return LPARAM of the message
+     */
+    public long getLPARAM() {
+        return LPARAM;
+    }
+
+    /**
+     * <p>
+     * Returns the WPARAM of a message.
+     * </p>
+     * 
+     * @return WPARAM of the message
+     */
+    public long getWPARAM() {
+        return WPARAM;
+    }
+
+    /**
+     * <p>
+     * Returns the number of parameters stored together with this message.
+     * </p>
+     * 
+     * @return number of parameters stored with this message
+     */
+    public int getNumParams() {
+        return params.size();
+    }
+
+    /**
+     * <p>
+     * Returns the parameters associated with this message.
+     * </p>
+     * 
+     * @return the parameters
+     */
+    protected Map<String, Object> getParameters() {
+        return params;
+    }
+
+    /**
+     * <p>
+     * Returns the XML target description of this message.
+     * </p>
+     * 
+     * @return the XML target description
+     */
+    public String getTargetXML() {
+        return targetXML;
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsMessageType.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsMessageType.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsMessageType.java	(revision 922)
@@ -0,0 +1,237 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.eventcore;
+
+/**
+ * <p>
+ * Enumeration to deal with MFC message types.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms, Steffen Herbold
+ */
+public enum WindowsMessageType {
+
+    WM_NULL(0), WM_CREATE(1), WM_DESTROY(2), WM_MOVE(3), WM_SIZE(5), WM_ACTIVATE(6),
+    WM_SETFOCUS(7), WM_KILLFOCUS(8), WM_ENABLE(10), WM_SETREDRAW(11), WM_SETTEXT(12),
+    WM_GETTEXT(13), WM_GETTEXTLENGTH(14), WM_PAINT(15), WM_CLOSE(16), WM_QUERYENDSESSION(17),
+    WM_QUIT(18), WM_QUERYOPEN(19), WM_ERASEBKGND(20), WM_SYSCOLORCHANGE(21), WM_ENDSESSION(22),
+    WM_SHOWWINDOW(24), WM_CTLCOLOR(25), WM_WININICHANGE(26), WM_DEVMODECHANGE(27), WM_ACTIVATEAPP(
+        28), WM_FONTCHANGE(29), WM_TIMECHANGE(30), WM_CANCELMODE(31), WM_SETCURSOR(32),
+    WM_MOUSEACTIVATE(33), WM_CHILDACTIVATE(34), WM_QUEUESYNC(35), WM_GETMINMAXINFO(36),
+    WM_PAINTICON(38), WM_ICONERASEBKGND(39), WM_NEXTDLGCTL(40), WM_SPOOLERSTATUS(42), WM_DRAWITEM(
+        43), WM_MEASUREITEM(44), WM_DELETEITEM(45), WM_VKEYTOITEM(46), WM_CHARTOITEM(47),
+    WM_SETFONT(48), WM_GETFONT(49), WM_SETHOTKEY(50), WM_GETHOTKEY(51), WM_QUERYDRAGICON(55),
+    WM_COMPAREITEM(57), WM_GETOBJECT(61), WM_COMPACTING(65), WM_COMMNOTIFY(68),
+    WM_WINDOWPOSCHANGING(70), WM_WINDOWPOSCHANGED(71), WM_POWER(72), WM_COPYDATA(74),
+    WM_CANCELJOURNAL(75), WM_NOTIFY(78), WM_INPUTLANGCHANGEREQUEST(80), WM_INPUTLANGCHANGE(81),
+    WM_TCARD(82), WM_HELP(83), WM_USERCHANGED(84), WM_NOTIFYFORMAT(85), WM_CONTEXTMENU(123),
+    WM_STYLECHANGING(124), WM_STYLECHANGED(125), WM_DISPLAYCHANGE(126), WM_GETICON(127),
+    WM_SETICON(128), WM_NCCREATE(129), WM_NCDESTROY(130), WM_NCCALCSIZE(131), WM_NCHITTEST(132),
+    WM_NCPAINT(133), WM_NCACTIVATE(134), WM_GETDLGCODE(135), WM_SYNCPAINT(136),
+    WM_NCMOUSEMOVE(160), WM_NCLBUTTONDOWN(161), WM_NCLBUTTONUP(162), WM_NCLBUTTONDBLCLK(163),
+    WM_NCRBUTTONDOWN(164), WM_NCRBUTTONUP(165), WM_NCRBUTTONDBLCLK(166), WM_NCMBUTTONDOWN(167),
+    WM_NCMBUTTONUP(168), WM_NCMBUTTONDBLCLK(169), WM_NCXBUTTONDOWN(171), WM_NCXBUTTONUP(172),
+    WM_NCXBUTTONDBLCLK(173), SBM_SETPOS(224), BM_CLICK(245), WM_INPUT(255), WM_KEYDOWN(256),
+    WM_KEYFIRST(256), WM_KEYUP(257), WM_CHAR(258), WM_DEADCHAR(259), WM_SYSKEYDOWN(260),
+    WM_SYSKEYUP(261), WM_SYSCHAR(262), WM_SYSDEADCHAR(263), WM_KEYLAST(264),
+    WM_WNT_CONVERTREQUESTEX(265), WM_CONVERTREQUEST(266), WM_CONVERTRESULT(267), WM_INTERIM(268),
+    WM_IME_STARTCOMPOSITION(269), WM_IME_ENDCOMPOSITION(270), WM_IME_COMPOSITION(271),
+    WM_IME_KEYLAST(271), WM_INITDIALOG(272), WM_COMMAND(273), WM_SYSCOMMAND(274), WM_TIMER(275),
+    WM_HSCROLL(276), WM_VSCROLL(277), WM_INITMENU(278), WM_INITMENUPOPUP(279), WM_MENUSELECT(287),
+    WM_MENUCHAR(288), WM_ENTERIDLE(289), WM_MENURBUTTONUP(290), WM_MENUDRAG(291), WM_MENUGETOBJECT(
+        292), WM_UNINTMENUPOPUP(293), WM_MENUCOMMAND(294), WM_CHANGEUISTATE(295), WM_UPDATEUISTATE(
+        296), WM_QUERYUISTATE(297), WM_CTLCOLORMSGBOX(306), WM_CTLCOLOREDIT(307),
+    WM_CTLCOLORLISTBOX(308), WM_CTLCOLORBTN(309), WM_CTLCOLORDLG(310), WM_CTLCOLORSCROLLBAR(311),
+    WM_CTLCOLORSTATIC(312), CB_SHOWDROPDOWN(335), LB_SETCURSEL(390), WM_MOUSEFIRST(512),
+    WM_MOUSEMOVE(512), WM_LBUTTONDOWN(513), WM_LBUTTONUP(514), WM_LBUTTONDBLCLK(515),
+    WM_RBUTTONDOWN(516), WM_RBUTTONUP(517), WM_RBUTTONDBLCLK(518), WM_MBUTTONDOWN(519),
+    WM_MBUTTONUP(520), WM_MBUTTONDBLCLK(521), WM_MOUSELAST(521), WM_MOUSEWHEEL(522),
+    WM_XBUTTONDOWN(523), WM_XBUTTONUP(524), WM_XBUTTONDBLCLK(525), WM_USER(1024),
+    CB_SETCURSEL(334), TBM_SETPOS(1029), UDM_SETRANGE(1125), TCM_SETCURSEL(4876);
+
+    /**
+     * <p>
+     * Numerical representation of the message type.
+     * </p>
+     */
+    private int mNumber;
+
+    /**
+     * <p>
+     * Constructor. Creates a new WindowsMessageType.
+     * </p>
+     * 
+     * @param number
+     *            numerical representation of the message type
+     */
+    WindowsMessageType(int number) {
+        mNumber = number;
+    }
+
+    /**
+     * <p>
+     * Returns the numerical representation of the message type.
+     * </p>
+     * 
+     * @return the numerical representation
+     */
+    public int getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * <p>
+     * Checks if the type of a message generated is a keyboard interaction.
+     * </p>
+     * 
+     * @param msgType
+     *            type of the message
+     * @return true if it is a keyboard interaction; false otherwise
+     */
+    public boolean isKeyMessage() {
+        boolean isKeyMsg = false;
+        switch (this)
+        {
+            case WM_KEYDOWN:
+            case WM_KEYUP:
+            case WM_SYSKEYDOWN:
+            case WM_SYSKEYUP:
+                isKeyMsg = true;
+                break;
+            default:
+                break;
+        }
+        return isKeyMsg;
+    }
+
+    /**
+     * <p>
+     * Checks if the type of a message indicates that the mouse has been pressed down.
+     * </p>
+     * 
+     * @param msgType
+     *            type of the message
+     * @return true if it is mouse-down message; false otherwise
+     */
+    public boolean isDownMessage() {
+        boolean isDownMsg = false;
+        switch (this)
+        {
+            case WM_LBUTTONDOWN:
+            case WM_RBUTTONDOWN:
+            case WM_MBUTTONDOWN:
+            case WM_XBUTTONDOWN:
+            case WM_NCLBUTTONDOWN:
+            case WM_NCRBUTTONDOWN:
+            case WM_NCMBUTTONDOWN:
+            case WM_NCXBUTTONDOWN:
+                isDownMsg = true;
+                break;
+            default:
+                break;
+        }
+        return isDownMsg;
+    }
+
+    /**
+     * <p>
+     * Checks if the type of a message indicates that a double click has been performed.
+     * </p>
+     * 
+     * @param msgType
+     *            type of the message
+     * @return true if it is a double click message; false otherwise
+     */
+    public boolean isDblclkMessage() {
+        boolean isDblclkMsg = false;
+        switch (this)
+        {
+            case WM_LBUTTONDBLCLK:
+            case WM_RBUTTONDBLCLK:
+            case WM_MBUTTONDBLCLK:
+            case WM_XBUTTONDBLCLK:
+            case WM_NCLBUTTONDBLCLK:
+            case WM_NCRBUTTONDBLCLK:
+            case WM_NCMBUTTONDBLCLK:
+            case WM_NCXBUTTONDBLCLK:
+                isDblclkMsg = true;
+                break;
+            default:
+                break;
+        }
+        return isDblclkMsg;
+    }
+
+    /**
+     * <p>
+     * Checks if the type of a message indicates that the mouse has been released.
+     * </p>
+     * 
+     * @param msgType
+     *            type of the message
+     * @return true if it is mouse-up message; false otherwise
+     */
+    public boolean isUpMessage() {
+        boolean isUpMsg = false;
+        switch (this)
+        {
+            case WM_LBUTTONUP:
+            case WM_RBUTTONUP:
+            case WM_MBUTTONUP:
+            case WM_XBUTTONUP:
+            case WM_NCLBUTTONUP:
+            case WM_NCRBUTTONUP:
+            case WM_NCMBUTTONUP:
+            case WM_NCXBUTTONUP:
+                isUpMsg = true;
+                break;
+            default:
+                break;
+        }
+        return isUpMsg;
+    }
+
+    /**
+     * <p>
+     * Parses an {@link String} and returns the respective WindowsMessageType if possible.
+     * </p>
+     * 
+     * @param numberString
+     *            String representation of the event type
+     * @return created WindowsMessageType
+     * @throws IllegalArgumentException
+     *             thrown if there is no WindowsMessageType that correlates to numberString
+     */
+    public static WindowsMessageType parseMessageType(String numberString)
+        throws IllegalArgumentException
+    {
+        try {
+            int number = Integer.parseInt(numberString);
+            return valueOf(number);
+        }
+        catch (NumberFormatException e) {
+            return WindowsMessageType.valueOf(WindowsMessageType.class, numberString);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the WindowsMessageType associated with an integer.
+     * </p>
+     * 
+     * @param number
+     *            integer to which the according WindowsMessageType is returned
+     * @return the WindowsMessageType
+     * @throws IllegalArgumentException
+     *             thrown if there is no WindowsMessageType that correlates to number
+     */
+    public static WindowsMessageType valueOf(int number) throws IllegalArgumentException {
+        for (WindowsMessageType type : WindowsMessageType.values()) {
+            if (type.mNumber == number) {
+                return type;
+            }
+        }
+
+        throw new IllegalArgumentException("there is no message type with number " + number);
+    }
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsVirtualKey.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsVirtualKey.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/eventcore/WindowsVirtualKey.java	(revision 922)
@@ -0,0 +1,262 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.eventcore;
+
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+
+/**
+ * <p>
+ * Map of virtual keys for MFC.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public enum WindowsVirtualKey {
+    // VK_LBUTTON (0x01, "Left mouse button"),
+    // VK_RBUTTON (0x02, "Right mouse button"),
+    VK_CANCEL(0x03, VirtualKey.CANCEL),
+    // VK_MBUTTON (0x04, "Middle mouse button (three-button mouse)"),
+    // VK_XBUTTON1 (0x05, "X1 mouse button"),
+    // VK_XBUTTON2 (0x06, "X2 mouse button"),
+    // - (0x07, "Undefined"),
+    VK_BACK(0x08, VirtualKey.BACK_SPACE),
+    VK_TAB(0x09, VirtualKey.TAB),
+    // - (0x0A-0B, "Reserved"),
+    VK_CLEAR(0x0C, VirtualKey.CLEAR),
+    VK_RETURN(0x0D, VirtualKey.ENTER),
+    // - (0x0E-0F, "Undefined"),
+    VK_SHIFT(0x10, VirtualKey.SHIFT),
+    VK_CONTROL(0x11, VirtualKey.CONTROL),
+    VK_MENU(0x12, VirtualKey.ALT),
+    VK_PAUSE(0x13, VirtualKey.PAUSE),
+    VK_CAPITAL(0x14, VirtualKey.CAPS_LOCK),
+    VK_KANA(0x15, VirtualKey.KANA),
+    // VK_HANGUEL (0x15, VirtualKey.HANGUEL),
+    // VK_HANGUL (0x15, "IME Hangul mode"),
+    // - (0x16, "Undefined"),
+    // VK_JUNJA (0x17, VirtualKey.J),
+    VK_FINAL(0x18, VirtualKey.FINAL),
+    // VK_HANJA (0x19, "IME Hanja mode"),
+    VK_KANJI(0x19, VirtualKey.KANJI),
+    // - (0x1A, "Undefined"),
+    VK_ESCAPE(0x1B, VirtualKey.ESCAPE), VK_CONVERT(0x1C, VirtualKey.CONVERT),
+    VK_NONCONVERT(0x1D, VirtualKey.NONCONVERT),
+    VK_ACCEPT(0x1E, VirtualKey.ACCEPT),
+    VK_MODECHANGE(0x1F, VirtualKey.MODECHANGE),
+
+    VK_SPACE(0x20, VirtualKey.SPACE),
+    VK_PRIOR(0x21, VirtualKey.PAGE_UP),
+    VK_NEXT(0x22, VirtualKey.PAGE_DOWN),
+    VK_END(0x23, VirtualKey.END),
+    VK_HOME(0x24, VirtualKey.HOME),
+    VK_LEFT(0x25, VirtualKey.LEFT),
+    VK_UP(0x26, VirtualKey.UP),
+    VK_RIGHT(0x27, VirtualKey.RIGHT),
+    VK_DOWN(0x28, VirtualKey.DOWN),
+    // VK_SELECT (0x29, VirtualKey.),
+    VK_PRINT(0x2A, VirtualKey.PRINTSCREEN),
+    // VK_EXECUTE (0x2B, VirtualKey.EXECUTE),
+    VK_SNAPSHOT(0x2C, VirtualKey.PRINTSCREEN), VK_INSERT(0x2D, VirtualKey.INSERT), VK_DELETE(0x2E,
+        VirtualKey.DELETE), VK_HELP(0x2F, VirtualKey.HELP), DIGIT_0(0x30, VirtualKey.DIGIT_0),
+    DIGIT_1(0x31, VirtualKey.DIGIT_1), DIGIT_2(0x32, VirtualKey.DIGIT_2), DIGIT_3(0x33,
+        VirtualKey.DIGIT_3), DIGIT_4(0x34, VirtualKey.DIGIT_4), DIGIT_5(0x35, VirtualKey.DIGIT_5),
+    DIGIT_6(0x36, VirtualKey.DIGIT_6),
+    DIGIT_7(0x37, VirtualKey.DIGIT_7),
+    DIGIT_8(0x38, VirtualKey.DIGIT_8),
+    DIGIT_9(0x39, VirtualKey.DIGIT_9),
+    // - (0x3A-40, "Undefined"),
+    A(0x41, VirtualKey.LETTER_A), B(0x42, VirtualKey.LETTER_B), C(0x43, VirtualKey.LETTER_C), D(
+        0x44, VirtualKey.LETTER_D), E(0x45, VirtualKey.LETTER_E), F(0x46, VirtualKey.LETTER_F), G(
+        0x47, VirtualKey.LETTER_G), H(0x48, VirtualKey.LETTER_H), I(0x49, VirtualKey.LETTER_I), J(
+        0x4A, VirtualKey.LETTER_J), K(0x4B, VirtualKey.LETTER_K), L(0x4C, VirtualKey.LETTER_L), M(
+        0x4D, VirtualKey.LETTER_M), N(0x4E, VirtualKey.LETTER_N), O(0x4F, VirtualKey.LETTER_O), P(
+        0x50, VirtualKey.LETTER_P), Q(0x51, VirtualKey.LETTER_Q), R(0x52, VirtualKey.LETTER_R), S(
+        0x53, VirtualKey.LETTER_S),
+    T(0x54, VirtualKey.LETTER_T),
+    U(0x55, VirtualKey.LETTER_U),
+    V(0x56, VirtualKey.LETTER_V),
+    W(0x57, VirtualKey.LETTER_W),
+    X(0x58, VirtualKey.LETTER_X),
+    Y(0x59, VirtualKey.LETTER_Y),
+    Z(0x5A, VirtualKey.LETTER_Z),
+    VK_LWIN(0x5B, VirtualKey.WINDOWS),
+    VK_RWIN(0x5C, VirtualKey.WINDOWS),
+    // VK_APPS (0x5D, "Applications key (Natural keyboard)"),
+    // - (0x5E, "Reserved"),
+    // VK_SLEEP (0x5F, VirtualKey.SLEEP),
+    VK_NUMPAD0(0x60, VirtualKey.NUMPAD_0), VK_NUMPAD1(0x61, VirtualKey.NUMPAD_1), VK_NUMPAD2(0x62,
+        VirtualKey.NUMPAD_2), VK_NUMPAD3(0x63, VirtualKey.NUMPAD_3), VK_NUMPAD4(0x64,
+        VirtualKey.NUMPAD_4), VK_NUMPAD5(0x65, VirtualKey.NUMPAD_5), VK_NUMPAD6(0x66,
+        VirtualKey.NUMPAD_6), VK_NUMPAD7(0x67, VirtualKey.NUMPAD_7), VK_NUMPAD8(0x68,
+        VirtualKey.NUMPAD_8), VK_NUMPAD9(0x69, VirtualKey.NUMPAD_9), VK_MULTIPLY(0x6A,
+        VirtualKey.MULTIPLY), VK_ADD(0x6B, VirtualKey.ADD),
+    VK_SEPARATOR(0x6C, VirtualKey.SEPARATOR), VK_SUBTRACT(0x6D, VirtualKey.SUBTRACT), VK_DECIMAL(
+        0x6E, VirtualKey.DECIMAL), VK_DIVIDE(0x6F, VirtualKey.DIVIDE), VK_F1(0x70, VirtualKey.F1),
+    VK_F2(0x71, VirtualKey.F2), VK_F3(0x72, VirtualKey.F3), VK_F4(0x73, VirtualKey.F4), VK_F5(0x74,
+        VirtualKey.F5), VK_F6(0x75, VirtualKey.F6), VK_F7(0x76, VirtualKey.F7), VK_F8(0x77,
+        VirtualKey.F8), VK_F9(0x78, VirtualKey.F9), VK_F10(0x79, VirtualKey.F10), VK_F11(0x7A,
+        VirtualKey.F11), VK_F12(0x7B, VirtualKey.F12), VK_F13(0x7C, VirtualKey.F13), VK_F14(0x7D,
+        VirtualKey.F14), VK_F15(0x7E, VirtualKey.F15), VK_F16(0x7F, VirtualKey.F16), VK_F17(0x80,
+        VirtualKey.F17), VK_F18(0x81, VirtualKey.F18), VK_F19(0x82, VirtualKey.F19), VK_F20(0x83,
+        VirtualKey.F20), VK_F21(0x84, VirtualKey.F21), VK_F22(0x85, VirtualKey.F22), VK_F23(0x86,
+        VirtualKey.F23), VK_F24(0x87, VirtualKey.F24),
+    // - (0x88-8F, "Unassigned"),
+    VK_NUMLOCK(0x90, VirtualKey.NUM_LOCK),
+    VK_SCROLL(0x91, VirtualKey.SCROLL_LOCK),
+    // - (0x92-96, "OEM specific"),
+    // - (0x97-9F, "Unassigned"),
+    VK_LSHIFT(0xA0, VirtualKey.SHIFT), VK_RSHIFT(0xA1, VirtualKey.SHIFT), VK_LCONTROL(0xA2,
+        VirtualKey.CONTROL), VK_RCONTROL(0xA3, VirtualKey.CONTROL), VK_LMENU(0xA4, VirtualKey.ALT),
+    VK_RMENU(0xA5, VirtualKey.ALT_GRAPH),
+    // VK_BROWSER_BACK (0xA6, VirtualKey.BROWSER_BACK),
+    // VK_BROWSER_FORWARD (0xA7, VirtualKey.BROWSER_FORWARD),
+    VK_BROWSER_REFRESH(0xA8, VirtualKey.F5), VK_BROWSER_STOP(0xA9, VirtualKey.STOP),
+    // VK_BROWSER_SEARCH (0xAA, VirtualKey.BROWSER_SEARCH),
+    // VK_BROWSER_FAVORITES (0xAB, VirtualKey.BROWSER_FAVORITES),
+    // VK_BROWSER_HOME (0xAC, VirtualKey.BROWSER_HOME),
+    // VK_VOLUME_MUTE (0xAD, VirtualKey.VOLUME_MUTE),
+    // VK_VOLUME_DOWN (0xAE, VirtualKey.VOLUME_DOWN),
+    // VK_VOLUME_UP (0xAF, VirtualKey.VOLUME_UP),
+    // VK_MEDIA_NEXT_TRACK (0xB0, VirtualKey.MEDIA_NEXT_TRACK),
+    // VK_MEDIA_PREV_TRACK (0xB1, VirtualKey.MEDIA_PREV_TRACK),
+    // VK_MEDIA_STOP (0xB2, VirtualKey.MEDIA_STOP),
+    // VK_MEDIA_PLAY_PAUSE (0xB3, VirtualKey.MEDIA_PLAY_PAUSE),
+    // VK_LAUNCH_MAIL (0xB4, VirtualKey.LAUNCH_MAIL),
+    // VK_LAUNCH_MEDIA_SELECT (0xB5, VirtualKey.LAUNCH_MEDIA_SELECT),
+    // VK_LAUNCH_APP1 (0xB6, VirtualKey.LAUNCH_APP1),
+    // VK_LAUNCH_APP2 (0xB7, VirtualKey.LAUNCH_APP2),
+    // - (0xB8-B9, "Reserved"),
+    // VK_OEM_1 (0xBA, "Used for miscellaneous characters; it can vary by keyboard." +
+    // "For the US standard keyboard, the ';:' key"),
+    VK_OEM_PLUS(0xBB, VirtualKey.PLUS), VK_OEM_COMMA(0xBC, VirtualKey.COMMA), VK_OEM_MINUS(0xBD,
+        VirtualKey.MINUS), VK_OEM_PERIOD(0xBE, VirtualKey.PERIOD);
+    // VK_OEM_2 (0xBF, "Used for miscellaneous characters; it can vary by keyboard." +
+    // "For the US standard keyboard, the '/?' key"),
+    // VK_OEM_3 (0xC0, "Used for miscellaneous characters; it can vary by keyboard." +
+    // "For the US standard keyboard, the '`~' key"),
+    // - (0xC1-D7, "ReserveD, "- (0xD8-DA, "Unassigned"),
+    // VK_OEM_4 (0xDB, "Used for miscellaneous characters; it can vary by keyboard." +
+    // "For the US standard keyboard, the '[{' key"),
+    // VK_OEM_5 (0xDC, "Used for miscellaneous characters; it can vary by keyboard." +
+    // "For the US standard keyboard, the '\\|' key"),
+    // VK_OEM_6 (0xDD, "Used for miscellaneous characters; it can vary by keyboard." +
+    // "For the US standard keyboard, the ']}' key"),
+    // VK_OEM_7 (0xDE, "Used for miscellaneous characters; it can vary by keyboard." +
+    // "For the US standard keyboard, the 'single-quote/double-quote' key"),
+    // VK_OEM_8 (0xDF, "Used for miscellaneous characters; it can vary by keyboard."),
+    // - (0xE0, "Reserved"),
+    // - (0xE1, "OEM specific"),
+    // VK_OEM_102 (0xE2, "Either the angle bracket key or the backslash key on the RT 102-key" +
+    // "keyboard"),
+    // - (0xE3-E4, "OEM specific"),
+    // VK_PROCESSKEY (0xE5, VirtualKey.EXECUTE),
+    // - (0xE6, "OEM specific"),
+    // VK_PACKET (0xE7, "Used to pass Unicode characters as if they were keystrokes. The " +
+    // "VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard " +
+    // "input methods. For more information, see Remark in KEYBDINPUT, SendInput, " +
+    // "WM_KEYDOWN, and WM_KEYUP"),
+    // - (0xE8, "Unassigned (0xE9-F5, "OEM specific"),
+    // VK_ATTN (0xF6, "Attn key"),
+    // VK_CRSEL (0xF7, "CrSel key"),
+    // VK_EXSEL (0xF8, "ExSel key"),
+    // VK_EREOF (0xF9, "Erase EOF key"),
+    // VK_PLAY (0xFA, VirtualKey.MEDIA_PLAY_PAUSE);
+    // VK_ZOOM (0xFB, "Zoom key"),
+    // VK_NONAME (0xFC, "Reserved"),
+    // VK_PA1 (0xFD, "PA1 key"),
+    // VK_OEM_CLEAR (0xFE, "Clear key");
+
+    /**
+     * <p>
+     * Numerical representation of the virtual key.
+     * </p>
+     */
+    private int mNumber;
+
+    /**
+     * <p>
+     * {@link VirtualKey} represented by this WindowsVirtualKey
+     * </p>
+     */
+    private VirtualKey mRepresentedKey;
+
+    /**
+     * <p>
+     * Constructor. Creates a new WindowsVirtualKey.
+     * </p>
+     * 
+     * @param number
+     *            numerical representation of the virtual key
+     * @param representedKey
+     *            virtual key that is represented
+     */
+    WindowsVirtualKey(int number, VirtualKey representedKey) {
+        mNumber = number;
+        mRepresentedKey = representedKey;
+    }
+
+    /**
+     * <p>
+     * Returns the numerical representation of the virtual key.
+     * </p>
+     * 
+     * @return the numerical representation
+     */
+    int getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * <p>
+     * Parses an {@link String} and returns the respective WindowsVirtualKey if possible.
+     * </p>
+     * 
+     * @param string
+     *            String representation of the event type
+     * @return created WindowsVirtualKey
+     * @throws IllegalArgumentException
+     *             thrown if there is no WindowsVirtualKey that correlates to string
+     */
+    public static WindowsVirtualKey parseVirtualKey(String string) throws IllegalArgumentException {
+        for (WindowsVirtualKey virtualKey : WindowsVirtualKey.values()) {
+            if (virtualKey.mNumber == Integer.parseInt(string)) {
+                return virtualKey;
+            }
+        }
+
+        throw new IllegalArgumentException("there is no virtual key with id " + string);
+    }
+
+    /**
+     * <p>
+     * Returns the WindowsVirtualKey associated with an integer.
+     * </p>
+     * 
+     * @param number
+     *            integer to which the according WindowsVirtualKey is returned
+     * @return the WindowsVirtualKey
+     * @throws IllegalArgumentException
+     *             thrown if there is no WindowsVirtualKey that correlates to number
+     */
+    public static WindowsVirtualKey valueOf(int number) throws IllegalArgumentException {
+        for (WindowsVirtualKey virtualKey : WindowsVirtualKey.values()) {
+            if (virtualKey.mNumber == number) {
+                return virtualKey;
+            }
+        }
+
+        throw new IllegalArgumentException("there is no virtual key with number " + number);
+    }
+
+    /**
+     * <p>
+     * Returns the {@link VirtualKey} associated with this WindowsVirtualKey.
+     * </p>
+     * 
+     * @return the virtual key
+     */
+    public VirtualKey getKey() {
+        return mRepresentedKey;
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCButton.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCButton.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCButton.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IButton;
+
+/**
+ * <p>
+ * Class that represents buttons in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCButton extends MFCGUIElement implements IButton {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCButton.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCButton(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCCanvas.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCCanvas.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCCanvas.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ICanvas;
+
+/**
+ * <p>
+ * Class that represents canvas' in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCCanvas extends MFCGUIElement implements ICanvas {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCCanvas.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCCanvas(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCComboBox.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCComboBox.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCComboBox.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IComboBox;
+
+/**
+ * <p>
+ * Class that represents combo boxes in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCComboBox extends MFCGUIElement implements IComboBox {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCComboBox.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCComboBox(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCDialog.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCDialog.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCDialog.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IDialog;
+
+/**
+ * <p>
+ * Class that represents dialogs in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCDialog extends MFCWindow implements IDialog {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCDialog.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCDialog(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCGUIElement.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCGUIElement.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCGUIElement.java	(revision 922)
@@ -0,0 +1,156 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.AbstractDefaultGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
+
+/**
+ * <p>
+ * Base class that represents GUI element in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public abstract class MFCGUIElement extends AbstractDefaultGUIElement {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCGUIElement.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCGUIElement(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getPlatform()
+     */
+    @Override
+    public String getPlatform() {
+        return "MFC";
+    }
+
+    /**
+     * <p>
+     * Returns the HWND (Id) of the GUI element.
+     * </p>
+     * 
+     * @return the HWND (Id)
+     */
+    public String getId() {
+        return Long.toString(((MFCGUIElementSpec) super.getSpecification()).getHwnd());
+    }
+
+    /**
+     * <p>
+     * Returns the type of the GUI element.
+     * </p>
+     * 
+     * @return the type
+     */
+    public String getType() {
+        return ((MFCGUIElementSpec) super.getSpecification()).getType();
+    }
+
+    /**
+     * <p>
+     * Returns the name of the GUI element.
+     * </p>
+     * 
+     * @return the name
+     */
+    public String getName() {
+        return ((MFCGUIElementSpec) super.getSpecification()).getName();
+    }
+
+    /**
+     * <p>
+     * Returns the modality of the GUI element.
+     * </p>
+     * 
+     * @return the modality
+     */
+    public boolean isModal() {
+        return ((MFCGUIElementSpec) super.getSpecification()).isModal();
+    }
+
+    /**
+     * <p>
+     * Returns the resource Id of the GUI element.
+     * </p>
+     * 
+     * @return the resource Id
+     */
+    public int getResourceId() {
+        return ((MFCGUIElementSpec) super.getSpecification()).getResourceId();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#updateSpecification(de.ugoe.cs.autoquest.eventcore
+     * .guimodel.IGUIElementSpec)
+     */
+    @Override
+    public void updateSpecification(IGUIElementSpec furtherSpec) {
+        ((MFCGUIElementSpec) super.getSpecification()).update(furtherSpec);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getStringIdentifier()
+     */
+    @Override
+    public String getStringIdentifier() {
+        String str = this.toString();
+        if (getParent() != null) {
+            return getParent().getStringIdentifier() + "->" + str;
+        }
+        return str;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return super.getSpecification().toString();
+    }
+
+    /**
+     * <p>
+     * Returns the XML representation of the GUI element.
+     * </p>
+     * 
+     * @return the XML representation
+     */
+    public String toXML() {
+        if (getParent() != null) {
+            return ((MFCGUIElement) getParent()).toXML() +
+                ((MFCGUIElementSpec) super.getSpecification()).toXML();
+        }
+        else {
+            return ((MFCGUIElementSpec) super.getSpecification()).toXML();
+        }
+    }
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCGUIElementSpec.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCGUIElementSpec.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCGUIElementSpec.java	(revision 922)
@@ -0,0 +1,403 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.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.
+ * </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.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class MFCGUIElementSpec implements IGUIElementSpec {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <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.autoquest.eventcore.guimodel.IGUIElementSpec#getType()
+     */
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * <p>
+     * Returns the modality of the specified GUI element.
+     * </p>
+     * 
+     * @return the modality
+     */
+    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.autoquest.eventcore.guimodel.IGUIElementSpec#getSimilarity(de.ugoe.cs.autoquest.eventcore
+     * .guimodel.IGUIElementSpec)
+     */
+    @Override
+    public boolean getSimilarity(IGUIElementSpec other) {
+
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof MFCGUIElementSpec)) {
+            return false;
+        }
+
+        MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other;
+
+        if ((type != otherSpec.type) && ((type != null) && (!type.equals(otherSpec.type)))) {
+            return false;
+        }
+
+        if (isModal != otherSpec.isModal) {
+            return false;
+        }
+
+        if (resourceId != otherSpec.resourceId) {
+            return false;
+        }
+
+        // up to now, we compared, if the basics match. Now lets compare the id and the
+        // name. Both may change. The name may be reset (e.g. the title of a frame using the
+        // asterisk in the case data was changed). The id may change if e.g. a dialog is closed
+        // and reopend, i.e. a new instance is created. If one of them stays the same, then
+        // similarity is given. Therefore these are the first two comparisons
+
+        if (hwnd == otherSpec.hwnd) {
+            return true;
+        }
+
+        if ((name != null) && (name.equals(otherSpec.name))) {
+            return true;
+        }
+
+        if ((((name == null) && (otherSpec.name == null)) || (("".equals(name)) && (""
+            .equals(otherSpec.name)))) &&
+            (formerNames.size() == 0) &&
+            (otherSpec.formerNames.size() == 0))
+        {
+            return true;
+        }
+
+        // if the hwnd and the name did not stay the same, then the name should be checked first.
+        // The current name of one of the specs must be contained in the former names of the
+        // respective other spec for similarity. Either of the specs should contain the name of the
+        // respective other spec in its former names. We can rely on this, as in the MFC context
+        // we get to know each name change. I.e. although currently the names of the specs differ,
+        // once they were identical. But it is sufficient to do it for the current names of the
+        // elements, as only one of them may have experienced more name changes then the other.
+
+        if ((otherSpec.name != null) && formerNames.contains(otherSpec.name)) {
+            return true;
+        }
+
+        if ((name != null) && otherSpec.formerNames.contains(name)) {
+            return true;
+        }
+
+        // ok. Even the names do not match. This is usually a clear indication, that the elements
+        // are distinct. However, we check, if the former handles matched. This is very unlikely
+        // to happen. But it may occur, if a GUI element does not have a name or its name stays
+        // the empty string and if this GUI element is created, destroyed, and created again.
+
+        if (formerHwnds.contains(otherSpec.hwnd) || otherSpec.formerHwnds.contains(hwnd)) {
+            return true;
+        }
+
+        // now we can be really sure, that the GUI elements differ
+
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec#equals(IGUIElementSpec)
+     */
+    @Override
+    public boolean equals(Object 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 identifier 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>
+     * Returns the XML representation of this specification.
+     * </p>
+     * 
+     * @return the XML representation
+     */
+    String toXML() {
+        return "<window name=\"" + (name != null ? StringTools.xmlEntityReplacement(name) : "") +
+            "\" class=\"" + StringTools.xmlEntityReplacement(type) + "\" resourceId=\"" +
+            resourceId + "\" isModal=\"" + isModal + "\"/>";
+    }
+
+    /**
+     * <p>
+     * Updates the specification with another specification.
+     * </p>
+     * 
+     * @param furtherSpec
+     *            specification used to update the current specification
+     */
+    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);
+            }
+        }
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCListBox.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCListBox.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCListBox.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IListBox;
+
+/**
+ * <p>
+ * Class that represents list boxes in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCListBox extends MFCGUIElement implements IListBox {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCListBox.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCListBox(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCPanel.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCPanel.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCPanel.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IPanel;
+
+/**
+ * <p>
+ * Class that represents panels in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCPanel extends MFCGUIElement implements IPanel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCPanel.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCPanel(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTabbedPane.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTabbedPane.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTabbedPane.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITabbedPane;
+
+/**
+ * <p>
+ * Class that represents tabbed panes in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCTabbedPane extends MFCGUIElement implements ITabbedPane {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new TabbedPane.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCTabbedPane(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTextArea.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTextArea.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTextArea.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
+
+/**
+ * <p>
+ * Class that represents text areas in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCTextArea extends MFCGUIElement implements ITextArea {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCTextArea.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCTextArea(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCToolBar.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCToolBar.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCToolBar.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IToolBar;
+
+/**
+ * <p>
+ * Class that represents tool bars in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCToolBar extends MFCGUIElement implements IToolBar {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCToolBar.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCToolBar(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTrackBar.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTrackBar.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCTrackBar.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITrackBar;
+
+/**
+ * <p>
+ * Class that represents track bars in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCTrackBar extends MFCGUIElement implements ITrackBar {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCTrackBar.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCTrackBar(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCWindow.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCWindow.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/MFCWindow.java	(revision 922)
@@ -0,0 +1,38 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IFrame;
+
+/**
+ * <p>
+ * Class that represents windows in MFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MFCWindow extends MFCGUIElement implements IFrame {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new MFCWindow.
+     * </p>
+     * 
+     * @param specification
+     *            specification of created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means that the element is a top-level
+     *            window
+     */
+    public MFCWindow(MFCGUIElementSpec specification, MFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/WindowTree.java
===================================================================
--- trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/WindowTree.java	(revision 922)
+++ trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/guimodel/WindowTree.java	(revision 922)
@@ -0,0 +1,291 @@
+
+package de.ugoe.cs.autoquest.plugin.mfc.guimodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementFactory;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementFactory;
+
+/**
+ * <p>
+ * This class provides an the interfaces for window trees.
+ * </p>
+ * <p>
+ * The window tree represents the hierarchical structure of the windows "as it is" currently during
+ * a session. It may change during the session due to creation and destruction of windows.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WindowTree {
+
+    /**
+     * <p>
+     * Maintains a set of all the targets of all widgets that were at some point part of the window
+     * tree.
+     * </p>
+     */
+    private Set<MFCGUIElementSpec> targets;
+
+    /**
+     * <p>
+     * Map of all GUI element specifications that are part of the tree for efficient searching. The
+     * keys of the map are the hwnd's of the GUI elements.
+     * </p>
+     */
+    private Map<Long, MFCGUIElementSpec> guiElementSpecs;
+
+    /**
+     * <p>
+     * Map of all children of GUI elements that are part of the tree. The keys of the map are the
+     * hwnd's of the parent GUI elements.
+     * </p>
+     */
+    private Map<Long, List<MFCGUIElementSpec>> childRelations;
+
+    /**
+     * <p>
+     * Map of all parents of GUI elements that are part of the tree. The keys of the map are the
+     * hwnd's of the child GUI elements.
+     * </p>
+     */
+    private Map<Long, MFCGUIElementSpec> 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 hwnd's of the GUI elements.
+     * </p>
+     */
+    private Map<Long, MFCGUIElement> guiElements;
+
+    /**
+     * <p>
+     * Creates a new WindowTree.
+     * </p>
+     * <p>
+     * Private, as the class is a singleton.
+     * </p>
+     */
+    public WindowTree() {
+        guiElementSpecs = new HashMap<Long, MFCGUIElementSpec>();
+        targets = new HashSet<MFCGUIElementSpec>();
+        childRelations = new HashMap<Long, List<MFCGUIElementSpec>>();
+        parentRelations = new HashMap<Long, MFCGUIElementSpec>();
+        guiElements = new HashMap<Long, MFCGUIElement>();
+    }
+
+    /**
+     * <p>
+     * Adds a new window to the tree.
+     * </p>
+     * 
+     * @param parentHwnd
+     *            hwnd of the parent window
+     * @param childHwnd
+     *            hwnd of the window to be created
+     * @param childWindowName
+     *            resource id of the window to be created
+     * @param resourceId
+     *            resource id of the window to be created
+     * @param className
+     *            class name of the window to be created
+     */
+    public void add(long parentHwnd,
+                    long childHwnd,
+                    String childWindowName,
+                    int resourceId,
+                    String className,
+                    boolean isModal)
+    {
+        MFCGUIElementSpec parent = guiElementSpecs.get(parentHwnd);
+        MFCGUIElementSpec child = guiElementSpecs.get(childHwnd);
+        if (child == null) {
+            child =
+                new MFCGUIElementSpec(childHwnd, childWindowName, resourceId, className, isModal);
+            if (parent != null) {
+                List<MFCGUIElementSpec> otherChildren = childRelations.get(parentHwnd);
+
+                if (otherChildren == null) {
+                    otherChildren = new ArrayList<MFCGUIElementSpec>();
+                    childRelations.put(parentHwnd, otherChildren);
+                }
+
+                otherChildren.add(child);
+
+                parentRelations.put(childHwnd, parent);
+            }
+            guiElementSpecs.put(childHwnd, child);
+            targets.add(child);
+        }
+    }
+
+    /**
+     * <p>
+     * Searches the tree for a window with the specified hwnd and returns its
+     * {@link MFCGUIElementSpec} .
+     * </p>
+     * 
+     * @param hwnd
+     *            hwnd that is looked for
+     * @return {@link MFCGUIElementSpec} of the window with the given hwnd if found, null otherwise
+     */
+    public MFCGUIElement find(long hwnd) {
+        MFCGUIElement guiElement = guiElements.get(hwnd);
+        if (guiElement == null) {
+            List<MFCGUIElementSpec> guiElementPath = new ArrayList<MFCGUIElementSpec>();
+
+            MFCGUIElementSpec child = guiElementSpecs.get(hwnd);
+
+            if (child == null) {
+                throw new RuntimeException("no GUI element found with id " + hwnd);
+            }
+
+            while (child != null) {
+                guiElementPath.add(0, child);
+                child = parentRelations.get(child.getHwnd());
+            }
+
+            try {
+                guiElement =
+                    (MFCGUIElement) guiModel.integratePath(guiElementPath, guiElementFactory);
+            }
+            catch (GUIModelException e) {
+                throw new RuntimeException("could not instantiate GUI element with id " + hwnd, e);
+            }
+            guiElements.put(hwnd, guiElement);
+        }
+        return guiElement;
+    }
+
+    /**
+     * <p>
+     * Sets the name of a GUI element given its HWND.
+     * </p>
+     * 
+     * @param hwnd
+     *            HWND of the GUI element
+     * @param windowName
+     *            new name of the GUI element
+     */
+    public void setName(long hwnd, String windowName) {
+        MFCGUIElementSpec child = guiElementSpecs.get(hwnd);
+        if (child != null) {
+            child.setName(windowName);
+
+            MFCGUIElement guiElement = guiElements.remove(hwnd);
+            if (guiElement == null) {
+                // we need to update the GUI model as well
+                find(hwnd);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Removes a window (defined by its hwnd) from the tree. All children of the window will be
+     * removed recursively.
+     * </p>
+     * 
+     * @param hwnd
+     *            hwnd of the window to be removed
+     * @return number of windows that were removed
+     */
+    public int remove(long hwnd) {
+        MFCGUIElementSpec node = guiElementSpecs.remove(hwnd);
+        int removedCounter = 1;
+
+        if (node != null) {
+            List<MFCGUIElementSpec> nodesToBeRemoved = childRelations.remove(hwnd);
+
+            // remove all children and sub-children, if any
+            if (nodesToBeRemoved != null) {
+                for (int i = 0; i < nodesToBeRemoved.size(); i++) {
+                    MFCGUIElementSpec nodeToBeRemoved = nodesToBeRemoved.get(i);
+                    List<MFCGUIElementSpec> children =
+                        childRelations.remove(nodeToBeRemoved.getHwnd());
+
+                    if (children != null) {
+                        nodesToBeRemoved.addAll(children);
+                    }
+
+                    guiElementSpecs.remove(nodeToBeRemoved.getHwnd());
+                    parentRelations.remove(nodeToBeRemoved.getHwnd());
+                    removedCounter++;
+                }
+            }
+
+            // the node may be a child node of a parent. So search for it and remove it
+            MFCGUIElementSpec parent = parentRelations.remove(hwnd);
+            if (parent != null) {
+                List<MFCGUIElementSpec> children = childRelations.get(parent.getHwnd());
+
+                if (children != null) {
+                    for (int i = 0; i < children.size(); i++) {
+                        if (children.get(i).getHwnd() == hwnd) {
+                            children.remove(i);
+                            break;
+                        }
+                    }
+
+                    if (children.size() <= 0) {
+                        childRelations.remove(parent.getHwnd());
+                    }
+                }
+            }
+        }
+        return removedCounter;
+    }
+
+    /**
+     * @return the guiModel
+     */
+    public GUIModel getGUIModel() {
+        return guiModel;
+    }
+
+    /**
+     * <p>
+     * Returns the number of nodes contained in the WindowTree.
+     * </p>
+     * 
+     * @return number of nodes
+     */
+    public int size() {
+        return guiElementSpecs.size();
+    }
+
+    /**
+     * <p>
+     * Returns a sorted set of all targets that existed any time in the window tree.
+     * </p>
+     * 
+     * @return set of targets
+     */
+    public Set<MFCGUIElementSpec> getTargets() {
+        return targets;
+    }
+
+}
