Index: trunk/autoquest-core-assertions-test/src/test/java/de/ugoe/cs/autoquest/assertions/FileEqualsReplayTest.java
===================================================================
--- trunk/autoquest-core-assertions-test/src/test/java/de/ugoe/cs/autoquest/assertions/FileEqualsReplayTest.java	(revision 922)
+++ trunk/autoquest-core-assertions-test/src/test/java/de/ugoe/cs/autoquest/assertions/FileEqualsReplayTest.java	(revision 922)
@@ -0,0 +1,71 @@
+package de.ugoe.cs.autoquest.assertions;
+
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.assertions.FileEqualsReplay;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>FileEqualsReplayTest</code> contains tests for the class
+ * <code>{@link FileEqualsReplay}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class FileEqualsReplayTest {
+
+	private final static String ENDLINE = System.getProperty("line.separator");
+
+	@Test
+	public void testFileEqualsReplay_1() throws Exception {
+		String expectedFile = "expectedFileString";
+		String actualFile = "actualFileString";
+
+		FileEqualsReplay result = new FileEqualsReplay(expectedFile, actualFile);
+
+		assertNotNull(result);
+		assertEquals(expectedFile, result.expectedFile);
+		assertEquals(actualFile, result.actualFile);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testFileEqualsReplay_2() throws Exception {
+		String actualFile = "actualFileString";
+
+		new FileEqualsReplay(null, actualFile);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testFileEqualsReplay_3() throws Exception {
+		String expectedFile = "expectedFileString";
+
+		new FileEqualsReplay(expectedFile, null);
+	}
+
+	@Test
+	public void testGetReplay_1() throws Exception {
+		FileEqualsReplay fixture = new FileEqualsReplay("", "");
+
+		String result = fixture.getReplay();
+
+		assertEquals("  <fileEquals actualFile=\"\" expectedFile=\"\"/>"
+				+ ENDLINE, result);
+	}
+
+	@Test
+	public void testGetReplay_2() throws Exception {
+
+		FileEqualsReplay fixture = new FileEqualsReplay("expectedFileString",
+				"actualFileString");
+
+		String result = fixture.getReplay();
+
+		assertEquals(
+				"  <fileEquals actualFile=\"actualFileString\" expectedFile=\"expectedFileString\"/>"
+						+ ENDLINE, result);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(FileEqualsReplayTest.class);
+	}
+}
Index: trunk/autoquest-core-assertions-test/src/test/java/de/ugoe/cs/autoquest/assertions/TextEqualsReplayTest.java
===================================================================
--- trunk/autoquest-core-assertions-test/src/test/java/de/ugoe/cs/autoquest/assertions/TextEqualsReplayTest.java	(revision 922)
+++ trunk/autoquest-core-assertions-test/src/test/java/de/ugoe/cs/autoquest/assertions/TextEqualsReplayTest.java	(revision 922)
@@ -0,0 +1,75 @@
+package de.ugoe.cs.autoquest.assertions;
+
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.assertions.TextEqualsReplay;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>TextEqualsReplayTest</code> contains tests for the class
+ * <code>{@link TextEqualsReplay}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class TextEqualsReplayTest {
+
+	private final static String ENDLINE = System.getProperty("line.separator");
+
+	@Test
+	public void testTextEqualsReplay_1() throws Exception {
+		String expectedValue = "expectedValueString";
+		String target = "targetString";
+
+		TextEqualsReplay result = new TextEqualsReplay(expectedValue, target);
+
+		assertNotNull(result);
+		assertEquals(expectedValue, result.expectedValue);
+		assertEquals(target, result.target);
+	}
+
+	@Test
+	public void testTextEqualsReplay_2() throws Exception {
+		String target = "targetString";
+
+		TextEqualsReplay result = new TextEqualsReplay(null, target);
+
+		assertNotNull(result);
+		assertEquals(null, result.expectedValue);
+		assertEquals(target, result.target);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testTextEqualsReplay_3() throws Exception {
+		String expectedValue = "expectedValueString";
+
+		new TextEqualsReplay(expectedValue, null);
+	}
+
+	@Test
+	public void testGetReplay_1() throws Exception {
+		TextEqualsReplay fixture = new TextEqualsReplay("", "");
+
+		String result = fixture.getReplay();
+
+		assertEquals(" <textEquals expectedValue=\"\">" + ENDLINE + "<target>"
+				+ ENDLINE + ENDLINE + "</target>" + ENDLINE + "</textEquals>"
+				+ ENDLINE, result);
+	}
+
+	@Test
+	public void testGetReplay_2() throws Exception {
+		TextEqualsReplay fixture = new TextEqualsReplay("expectedValueString",
+				"targetString");
+
+		String result = fixture.getReplay();
+
+		assertEquals(" <textEquals expectedValue=\"expectedValueString\">"
+				+ ENDLINE + "<target>" + ENDLINE + "targetString" + ENDLINE
+				+ "</target>" + ENDLINE + "</textEquals>" + ENDLINE, result);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(TextEqualsReplayTest.class);
+	}
+}
Index: trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/FileEqualsAssertEventType.java
===================================================================
--- trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/FileEqualsAssertEventType.java	(revision 922)
+++ trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/FileEqualsAssertEventType.java	(revision 922)
@@ -0,0 +1,33 @@
+
+package de.ugoe.cs.autoquest.assertions;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+/**
+ * <p>
+ * Event type for FileEquals assertions.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class FileEqualsAssertEventType implements IEventType {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventType#getName()
+     */
+    @Override
+    public String getName() {
+        return "FileEqualsAssertion";
+    }
+
+}
Index: trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/FileEqualsReplay.java
===================================================================
--- trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/FileEqualsReplay.java	(revision 922)
+++ trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/FileEqualsReplay.java	(revision 922)
@@ -0,0 +1,89 @@
+package de.ugoe.cs.autoquest.assertions;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.autoquest.eventcore.IReplayable;
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * This class defines the replay for file equals assertions.
+ * </p>
+ * 
+ * @author Jeffrey Hall, Steffen Herbold
+ * @version 2.0
+ */
+public class FileEqualsReplay implements IReplayable {
+
+    /**
+     * <p>
+     * The file that should be equal to expectedFile.
+     * </p>
+     */
+    protected final String actualFile;
+
+    /**
+     * <p>
+     * The file that is used as the reference.
+     * </p>
+     */
+    protected final String expectedFile;
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new FileEqualsReplay.
+     * </p>
+     * 
+     * @param expectedFile
+     *            name and path of the expected file
+     * @param actualFile
+     *            name and path of the actual file
+     * @throws IllegalArgumentException
+     *             thrown if expectedFile or actualFile are null
+     */
+    public FileEqualsReplay(String expectedFile, String actualFile) {
+        if (expectedFile == null) {
+            throw new IllegalArgumentException("expected file must not be null");
+        }
+        if (actualFile == null) {
+            throw new IllegalArgumentException("actual file must not be null");
+        }
+        this.expectedFile = expectedFile;
+        this.actualFile = actualFile;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getReplay()
+     */
+    public String getReplay() {
+
+        String actualFileTmp = StringTools.xmlEntityReplacement(actualFile);
+        String expectedFileTmp = StringTools.xmlEntityReplacement(expectedFile);
+
+        StringBuilder currentMsgStr = new StringBuilder(800);
+        currentMsgStr.append("  <fileEquals ");
+        currentMsgStr.append("actualFile=\"" + actualFileTmp + "\" ");
+        currentMsgStr.append("expectedFile=\"" + expectedFileTmp + "\"/>");
+        currentMsgStr.append(StringTools.ENDLINE);
+        return currentMsgStr.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getDecorator()
+     */
+    @Override
+    public IReplayDecorator getDecorator() {
+        return null;
+    }
+
+}
Index: trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/TextEqualsAssertEventType.java
===================================================================
--- trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/TextEqualsAssertEventType.java	(revision 922)
+++ trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/TextEqualsAssertEventType.java	(revision 922)
@@ -0,0 +1,31 @@
+
+package de.ugoe.cs.autoquest.assertions;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+/**
+ * </p> Event type for TextEquals assertions. </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class TextEqualsAssertEventType implements IEventType {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventType#getName()
+     */
+    @Override
+    public String getName() {
+        return "TextEqualsAssertion";
+    }
+
+}
Index: trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/TextEqualsReplay.java
===================================================================
--- trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/TextEqualsReplay.java	(revision 922)
+++ trunk/autoquest-core-assertions/src/main/java/de/ugoe/cs/autoquest/assertions/TextEqualsReplay.java	(revision 922)
@@ -0,0 +1,92 @@
+package de.ugoe.cs.autoquest.assertions;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.autoquest.eventcore.IReplayable;
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * This class defines the replay for a textEquals assertion.
+ * </p>
+ * 
+ * @author Jeffrey Hall, Steffen Herbold
+ * @version 2.0
+ */
+public class TextEqualsReplay implements IReplayable {
+
+    /**
+     * <p>
+     * Reference value which is compared to the targets text.
+     * </p>
+     */
+    protected final String expectedValue;
+
+    /**
+     * <p>
+     * Target to which the text is compared.
+     * </p>
+     */
+    protected final String target;
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new TextEqualsReplay.
+     * 
+     * @param expectedValue
+     *            expected string value
+     * @param target
+     *            string description of the target whose string value is compared to the expected
+     *            value
+     * @throws IllegalArgumentException
+     *             thrown if target is null
+     */
+    public TextEqualsReplay(String expectedValue, String target) {
+        if (target == null) {
+            throw new IllegalArgumentException("target must not be null");
+        }
+        this.expectedValue = expectedValue;
+        this.target = target;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getReplay()
+     */
+    @Override
+    public String getReplay() {
+
+        String expectedValueTmp = StringTools.xmlEntityReplacement(expectedValue);
+
+        StringBuilder currentMsgStr = new StringBuilder(400);
+        currentMsgStr.append(" <textEquals expectedValue=\"" + expectedValueTmp + "\">");
+        currentMsgStr.append(StringTools.ENDLINE);
+        currentMsgStr.append("<target>");
+        currentMsgStr.append(StringTools.ENDLINE);
+        currentMsgStr.append(target);
+        currentMsgStr.append(StringTools.ENDLINE);
+        currentMsgStr.append("</target>");
+        currentMsgStr.append(StringTools.ENDLINE);
+        currentMsgStr.append("</textEquals>");
+        currentMsgStr.append(StringTools.ENDLINE);
+        return currentMsgStr.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getDecorator()
+     */
+    @Override
+    public IReplayDecorator getDecorator() {
+        return null;
+    }
+
+}
Index: trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorObservedTest.java
===================================================================
--- trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorObservedTest.java	(revision 922)
+++ trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorObservedTest.java	(revision 922)
@@ -0,0 +1,315 @@
+package de.ugoe.cs.autoquest.coverage;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import de.ugoe.cs.autoquest.coverage.CoverageCalculatorObserved;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.MockTrieBasedModel;
+
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>CoverageCalculatorObservedTest</code> contains tests for the
+ * class <code>{@link CoverageCalculatorObserved}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CoverageCalculatorObservedTest {
+	
+	Collection<List<Event>> sequencesObserved;
+	
+	Set<List<Event>> sequencesCovered;
+	Set<List<Event>> sequencesCovered2;
+	Set<List<Event>> sequencesNewPossible;
+	
+	MockTrieBasedModel mockProcess;
+	
+	@Test
+	public void testCoverageCalculatorObserved_1() throws Exception {
+		int length = 2;
+
+		CoverageCalculatorObserved result = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		assertNotNull(result);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorObserved_2() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorObserved(null,sequencesCovered, length);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorObserved_3() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorObserved(sequencesObserved, null, length);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorObserved_4() throws Exception {
+		int length = 0;
+
+		new CoverageCalculatorObserved(sequencesObserved, sequencesCovered, length);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorObserved_5() throws Exception {
+		int length = -1;
+
+		new CoverageCalculatorObserved(sequencesObserved, sequencesCovered, length);
+	}
+	
+	@Test
+	public void testCoverageObserved_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		double result = fixture.getCoverageObserved();
+		
+		assertEquals(3.0/6.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testCoverageObserved_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered2, length);
+		
+		double result = fixture.getCoverageObserved();
+		
+		assertEquals(2.0/6.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testCoverageObservedWeigth_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		double result = fixture.getCoverageObservedWeigth(mockProcess);
+		
+		assertEquals(6.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testCoverageObservedWeigth_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered2, length);
+		
+		double result = fixture.getCoverageObservedWeigth(mockProcess);
+		
+		assertEquals(4.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testGetNewPercentage_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		double result = fixture.getNewPercentage();
+		
+		assertEquals(1.0/4.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testGetNewPercentage_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered2, length);
+		
+		double result = fixture.getNewPercentage();
+		
+		assertEquals(0.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testCoveragePossibleNew_1() throws Exception {
+		int length = 3;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesNewPossible, length);
+		
+		double result = fixture.getCoveragePossibleNew(mockProcess);
+		
+		assertEquals(1.0/11.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testCoveragePossibleNew_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		double result = fixture.getCoveragePossibleNew(mockProcess);
+		
+		assertEquals(0.0, result, 0.0001);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class ) 
+	public void testCoveragePossibleNew_3() throws Exception {
+		int length = 3;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		fixture.getCoveragePossibleNew(null);
+	}
+	
+	@Test
+	public void testCoveragePossibleNewWeight_1() throws Exception {
+		int length = 3;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesNewPossible, length);
+		
+		double result = fixture.getCoveragePossibleNewWeight(mockProcess);
+		
+		assertEquals(2.0, result, 0.0001);
+	}
+	
+	@Test
+	public void testCoveragePossibleNewWeight_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		double result = fixture.getCoveragePossibleNewWeight(mockProcess);
+		
+		assertEquals(0.0, result, 0.0001);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class ) 
+	public void testCoveragePossibleNewWeight_3() throws Exception {
+		int length = 3;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		fixture.getCoveragePossibleNewWeight(null);
+	}	
+	
+	
+	@Test
+	public void testGetNumObserved_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		int result = fixture.getNumObserved();
+		
+		assertEquals(6, result);
+	}
+	
+	@Test
+	public void testGetNumCovered_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		int result = fixture.getNumCovered();
+		
+		assertEquals(4, result);
+	}
+	
+	@Test
+	public void testGetNumCovered_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered2, length);
+		
+		int result = fixture.getNumCovered();
+		
+		assertEquals(2, result);
+	}
+	
+	@Test
+	public void testGetNumNew_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered, length);
+		
+		int result = fixture.getNumNew();
+		
+		assertEquals(1, result);
+	}
+	
+	@Test
+	public void testGetNumNew_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorObserved fixture = new CoverageCalculatorObserved(
+				sequencesObserved, sequencesCovered2, length);
+		
+		int result = fixture.getNumNew();
+		
+		assertEquals(0, result);
+	}
+	
+	@Before
+	public void setUp() throws Exception {
+		sequencesObserved = new LinkedList<List<Event>>();
+		List<Event> sequence1 = new ArrayList<Event>();
+		sequence1.add(new Event(new StringEventType("a")));
+		sequence1.add(new Event(new StringEventType("b")));
+		sequence1.add(new Event(new StringEventType("r")));
+		sequence1.add(new Event(new StringEventType("a")));
+		List<Event> sequence2 = new ArrayList<Event>();
+		sequence2.add(new Event(new StringEventType("c")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequence2.add(new Event(new StringEventType("d")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequence2.add(new Event(new StringEventType("b")));
+		sequence2.add(new Event(new StringEventType("r")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequencesObserved.add(sequence1);
+		sequencesObserved.add(sequence2);
+
+		sequencesCovered = new LinkedHashSet<List<Event>>();
+		List<Event> tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("b")));
+		tmpList.add(new Event(new StringEventType("r")));
+		sequencesCovered.add(tmpList);
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("b")));
+		tmpList.add(new Event(new StringEventType("r")));
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("e")));
+		sequencesCovered.add(tmpList);
+
+		sequencesCovered2 = new LinkedHashSet<List<Event>>();
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("b")));
+		tmpList.add(new Event(new StringEventType("r")));
+		sequencesCovered2.add(tmpList);
+		
+		sequencesNewPossible = new LinkedHashSet<List<Event>>();
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("r")));
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("b")));
+		sequencesNewPossible.add(tmpList);
+		
+		int markovOrder = 2;
+		mockProcess = new MockTrieBasedModel(markovOrder, new Random());
+		mockProcess.train(sequencesObserved);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore()
+				.run(CoverageCalculatorObservedTest.class);
+	}
+
+}
Index: trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorProcessTest.java
===================================================================
--- trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorProcessTest.java	(revision 922)
+++ trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorProcessTest.java	(revision 922)
@@ -0,0 +1,238 @@
+package de.ugoe.cs.autoquest.coverage;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.coverage.CoverageCalculatorProcess;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.MockTrieBasedModel;
+
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>CoverageCalculatorProcessTest</code> contains tests for the
+ * class <code>{@link CoverageCalculatorProcess}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CoverageCalculatorProcessTest {
+
+	Set<List<Event>> sequencesCovered;
+	Set<List<Event>> sequencesCovered2;
+	MockTrieBasedModel mockProcess;
+
+	@Test
+	public void testCoverageCalculatorProcess_1() throws Exception {
+		int length = 2;
+
+		CoverageCalculatorProcess result = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+		assertNotNull(result);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorProcess_2() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorProcess(null,sequencesCovered, length);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorProcess_3() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorProcess(mockProcess, null, length);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorProcess_4() throws Exception {
+		int length = 0;
+
+		new CoverageCalculatorProcess(mockProcess, sequencesCovered, length);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testCoverageCalculatorProcess_5() throws Exception {
+		int length = -1;
+
+		new CoverageCalculatorProcess(mockProcess, sequencesCovered, length);
+	}
+
+	@Test
+	public void testGetCoverageAllNoWeight_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+
+		double result = fixture.getCoverageAllNoWeight();
+
+		assertEquals(3.0 / 49.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetCoverageAllNoWeight_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered2, length);
+
+		double result = fixture.getCoverageAllNoWeight();
+
+		assertEquals(2.0 / 49.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetCoveragePossibleNoWeight_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+
+		double result = fixture.getCoveragePossibleNoWeight();
+
+		assertEquals(3.0 / 9.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetCoveragePossibleNoWeight_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered2, length);
+
+		double result = fixture.getCoveragePossibleNoWeight();
+
+		assertEquals(2.0 / 9.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetCoveragePossibleWeight_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+
+		double result = fixture.getCoveragePossibleWeight();
+
+		assertEquals(6.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetCoveragePossibleWeight_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered2, length);
+
+		double result = fixture.getCoveragePossibleWeight();
+
+		assertEquals(4.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetNumCovered_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+
+		int result = fixture.getNumCovered();
+
+		assertEquals(3, result);
+	}
+
+	@Test
+	public void testGetNumCovered_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered2, length);
+
+		int result = fixture.getNumCovered();
+
+		assertEquals(2, result);
+	}
+
+	@Test
+	public void testGetNumPossible_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+
+		int result = fixture.getNumPossible();
+
+		assertEquals(9, result);
+	}
+
+	@Test
+	public void testSetSequences_1() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+
+		fixture.setSequences(sequencesCovered2);
+
+		// testing indirectly if sequences were changed
+		assertEquals(2, fixture.getNumCovered());
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testSetSequences_2() throws Exception {
+		int length = 2;
+		CoverageCalculatorProcess fixture = new CoverageCalculatorProcess(
+				mockProcess, sequencesCovered, length);
+
+		fixture.setSequences(null);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		Collection<List<Event>> sequences = new LinkedList<List<Event>>();
+		List<Event> sequence1 = new ArrayList<Event>();
+		sequence1.add(new Event(new StringEventType("a")));
+		sequence1.add(new Event(new StringEventType("b")));
+		sequence1.add(new Event(new StringEventType("r")));
+		sequence1.add(new Event(new StringEventType("a")));
+		List<Event> sequence2 = new ArrayList<Event>();
+		sequence2.add(new Event(new StringEventType("c")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequence2.add(new Event(new StringEventType("d")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequence2.add(new Event(new StringEventType("b")));
+		sequence2.add(new Event(new StringEventType("r")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequences.add(sequence1);
+		sequences.add(sequence2);
+
+		sequencesCovered = new LinkedHashSet<List<Event>>();
+		List<Event> tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("b")));
+		tmpList.add(new Event(new StringEventType("r")));
+		sequencesCovered.add(tmpList);
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("b")));
+		tmpList.add(new Event(new StringEventType("r")));
+		tmpList.add(new Event(new StringEventType("a")));
+		sequencesCovered.add(tmpList);
+
+		sequencesCovered2 = new LinkedHashSet<List<Event>>();
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("b")));
+		tmpList.add(new Event(new StringEventType("r")));
+		sequencesCovered2.add(tmpList);
+
+		int markovOrder = 2;
+		mockProcess = new MockTrieBasedModel(markovOrder, new Random());
+		mockProcess.train(sequences);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore()
+				.run(CoverageCalculatorProcessTest.class);
+	}
+}
Index: trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/SequenceToolsTest.java
===================================================================
--- trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/SequenceToolsTest.java	(revision 922)
+++ trunk/autoquest-core-coverage-test/src/test/java/de/ugoe/cs/autoquest/coverage/SequenceToolsTest.java	(revision 922)
@@ -0,0 +1,203 @@
+package de.ugoe.cs.autoquest.coverage;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.coverage.SequenceTools;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.autoquest.usageprofiles.MockTrieBasedModel;
+
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>SequenceToolsTest</code> contains tests for the class <code>{@link SequenceTools}</code>.
+ *
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class SequenceToolsTest {
+	
+	Collection<List<Event>> sequences;
+	Set<List<Event>> subSequences;
+	MockTrieBasedModel mockProcess;
+	
+	@Test
+	public void testContainedSubSequences_1()
+		throws Exception {
+		int length = 2;
+
+		Set<List<Event>> result = SequenceTools.containedSubSequences(sequences, length);
+
+		assertNotNull(result);
+		assertTrue(result.containsAll(subSequences));
+		assertEquals(subSequences.size(), result.size());
+	}
+	
+	@Test
+	public void testContainedSubSequences_2()
+		throws Exception {
+		int length = 2;
+
+		Set<List<Event>> result = SequenceTools.containedSubSequences(null, length);
+		assertNotNull(result);
+		assertTrue(result.isEmpty());
+	}
+	
+	@Test(expected=java.lang.IllegalArgumentException.class)
+	public void testContainedSubSequences_3()
+		throws Exception {
+		int length = 0;
+
+		SequenceTools.containedSubSequences(sequences, length);
+	}
+	
+	@Test(expected=java.lang.IllegalArgumentException.class)
+	public void testContainedSubSequences_4()
+		throws Exception {
+		int length = -1;
+
+		SequenceTools.containedSubSequences(sequences, length);
+	}
+
+	@Test
+	public void testGenerateWeights_1()
+		throws Exception {
+		Map<List<Event>, Double> result = SequenceTools.generateWeights(mockProcess, subSequences);
+
+		assertNotNull(result);
+		Set<Entry<List<Event>, Double>> entrySet = result.entrySet();
+		assertEquals(subSequences.size(),entrySet.size());
+		for( Entry<List<Event>, Double> entry : entrySet ) {
+			assertEquals(Double.valueOf(2.0d), entry.getValue());
+			assertTrue(subSequences.contains(entry.getKey()));
+		}
+	}
+	
+	@Test
+	public void testGenerateWeights_2()
+		throws Exception {
+		Map<List<Event>, Double> result = SequenceTools.generateWeights(null, subSequences);
+		
+		Set<Entry<List<Event>, Double>> entrySet = result.entrySet();
+		assertEquals(subSequences.size(),entrySet.size());
+		for( Entry<List<Event>, Double> entry : entrySet ) {
+			assertEquals(Double.valueOf(0.0d), entry.getValue());
+			assertTrue(subSequences.contains(entry.getKey()));
+		}
+	}
+	
+	@Test
+	public void testGenerateWeights_3()
+		throws Exception {
+		Map<List<Event>, Double> result = SequenceTools.generateWeights(mockProcess, null);
+		
+		assertNotNull(result);
+		assertTrue(result.isEmpty());
+	}
+
+	@Test
+	public void testNumSequences_1()
+		throws Exception {
+		int length = 2;
+		int expected = 49;
+
+		long result = SequenceTools.numSequences(mockProcess, length);
+
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testNumSequences_2()
+		throws Exception {
+		int length = 2;
+		int expected = 49;
+
+		long result = SequenceTools.numSequences(mockProcess, length);
+
+		assertEquals(expected, result);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class )
+	public void testNumSequences_3()
+		throws Exception {
+		int length = 0;
+
+		SequenceTools.numSequences(mockProcess, length);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class )
+	public void testNumSequences_4()
+		throws Exception {
+		IStochasticProcess process = new FirstOrderMarkovModel(new Random());
+		int length = -1;
+
+		SequenceTools.numSequences(process, length);
+	}
+	
+	@Before
+	public void setUp()
+		throws Exception {
+		sequences = new LinkedList<List<Event>>();
+		List<Event> sequence1 = new ArrayList<Event>();
+		sequence1.add(new Event(new StringEventType("a")));
+		sequence1.add(new Event(new StringEventType("b")));
+		sequence1.add(new Event(new StringEventType("r")));
+		sequence1.add(new Event(new StringEventType("a")));
+		List<Event> sequence2 = new ArrayList<Event>();
+		sequence2.add(new Event(new StringEventType("c")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequence2.add(new Event(new StringEventType("d")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequence2.add(new Event(new StringEventType("b")));
+		sequence2.add(new Event(new StringEventType("r")));
+		sequence2.add(new Event(new StringEventType("a")));
+		sequences.add(sequence1);
+		sequences.add(sequence2);
+		
+		subSequences = new LinkedHashSet<List<Event>>();
+		List<Event> tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("b")));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("b")));
+		tmpList.add(new Event(new StringEventType("r")));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("r")));
+		tmpList.add(new Event(new StringEventType("a")));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("c")));
+		tmpList.add(new Event(new StringEventType("a")));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("a")));
+		tmpList.add(new Event(new StringEventType("d")));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event>();
+		tmpList.add(new Event(new StringEventType("d")));
+		tmpList.add(new Event(new StringEventType("a")));
+		subSequences.add(tmpList);
+		
+		int markovOrder = 2;
+		mockProcess = new MockTrieBasedModel(markovOrder, new Random());
+		mockProcess.train(sequences);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(SequenceToolsTest.class);
+	}
+}
Index: trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorObserved.java
===================================================================
--- trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorObserved.java	(revision 922)
+++ trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorObserved.java	(revision 922)
@@ -0,0 +1,258 @@
+package de.ugoe.cs.autoquest.coverage;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+
+/**
+ * <p>
+ * This class calculates various types of sequence coverage in relation to a collection of observed
+ * sequences.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CoverageCalculatorObserved {
+
+    /**
+     * <p>
+     * Sequences for which the coverage is calculated.
+     * </p>
+     */
+    private final Collection<List<Event>> sequences;
+
+    /**
+     * <p>
+     * Observed sequences that are baseline for the coverage calculation.
+     * </p>
+     */
+    private final Collection<List<Event>> observedSequences;
+
+    /**
+     * <p>
+     * Length of the subsequences in relation to which the covarage is calculated.
+     * </p>
+     */
+    private final int length;
+
+    /**
+     * <p>
+     * All subsequences of {@link #length} of {@link #sequences}.
+     * </p>
+     */
+    private Collection<List<Event>> subSeqsGenerated = null;
+
+    /**
+     * <p>
+     * All subsequences of {@link #length} of {@link #observedSequences}.
+     * </p>
+     */
+    private Collection<List<Event>> subSeqsObserved = null;
+
+    /**
+     * <p>
+     * Constructor. Creates a new CoverageCalculatorObserved for given collections of observed
+     * sequences and generated sequences.
+     * </p>
+     * 
+     * @param observedSequences
+     *            observed sequences in relation to which the coverage is calculated; must not be
+     *            null
+     * @param sequences
+     *            sequences for which the coverage is calculated; must not be null
+     * @param length
+     *            length of the subsequences for which the coverage is analyzed; must be >0
+     * @throws IllegalArgumentException
+     *             thrown if observedSequences or sequences is null or length less than or equal to
+     *             0
+     */
+    public CoverageCalculatorObserved(Collection<List<Event>> observedSequences,
+                                      Collection<List<Event>> sequences,
+                                      int length)
+    {
+        if (observedSequences == null) {
+            throw new IllegalArgumentException("observed sequences must not be null");
+        }
+        if (sequences == null) {
+            throw new IllegalArgumentException("sequences must not be null");
+        }
+        if (length <= 0) {
+            throw new IllegalArgumentException("length must be >0; actual value: " + length);
+        }
+        this.observedSequences = observedSequences;
+        this.sequences = sequences;
+        this.length = length;
+    }
+
+    /**
+     * <p>
+     * Calculates the percentage of subsequences of length k that occur, with reference to those
+     * that were observed.
+     * </p>
+     * 
+     * @return coverage percentage
+     */
+    public double getCoverageObserved() {
+        createSubSeqs();
+        Collection<List<Event>> subSeqsObservedCopy =
+            new LinkedHashSet<List<Event>>(subSeqsObserved);
+        subSeqsObservedCopy.retainAll(subSeqsGenerated);
+        return ((double) subSeqsObservedCopy.size()) / subSeqsObserved.size();
+    }
+
+    /**
+     * <p>
+     * Calculates the weight of subsequences of length k that occur, with reference to those that
+     * were observed.
+     * </p>
+     * 
+     * @param process
+     *            stochastic process in reference to which the weight is calculated
+     * @return coverage percentage
+     */
+
+    public double getCoverageObservedWeigth(IStochasticProcess process) {
+        createSubSeqs();
+        Map<List<Event>, Double> weightMap =
+            SequenceTools.generateWeights(process, subSeqsObserved);
+
+        Collection<List<Event>> subSeqsObservedCopy =
+            new LinkedHashSet<List<Event>>(subSeqsObserved);
+        subSeqsObservedCopy.retainAll(subSeqsGenerated);
+        double weight = 0.0d;
+        for (List<Event> subSeq : subSeqsObservedCopy) {
+            weight += weightMap.get(subSeq);
+        }
+        return weight;
+    }
+
+    /**
+     * <p>
+     * Calculates the percentage of generated subsequences of length k that occur and have not been
+     * observed, with reference to all generated subsequences.
+     * </p>
+     * 
+     * @return coverage percentage
+     */
+    public double getNewPercentage() {
+        createSubSeqs();
+        Collection<List<Event>> subSeqsGeneratedCopy =
+            new LinkedHashSet<List<Event>>(subSeqsGenerated);
+        subSeqsGeneratedCopy.removeAll(subSeqsObserved);
+        return ((double) subSeqsGeneratedCopy.size()) / subSeqsGenerated.size();
+    }
+
+    /**
+     * <p>
+     * Calculates the percentage of generated subsequences of length k that occur and have not been
+     * observed, with references to all possible new subsequences.
+     * </p>
+     * 
+     * @param process
+     *            stochastic process which is used to determine which subsequences are possible
+     * @return coverage percentage
+     * @throws IllegalArgumentException
+     *             thrown if process is null
+     */
+    public double getCoveragePossibleNew(IStochasticProcess process) {
+        if (process == null) {
+            throw new IllegalArgumentException("process must not be null");
+        }
+        createSubSeqs();
+        Collection<List<Event>> subSeqsGeneratedCopy =
+            new LinkedHashSet<List<Event>>(subSeqsGenerated);
+        Collection<List<Event>> subSeqsPossible = process.generateSequences(length);
+        subSeqsGeneratedCopy.removeAll(subSeqsObserved);
+        subSeqsPossible.removeAll(subSeqsObserved);
+        int possibleSize = subSeqsPossible.size();
+        subSeqsPossible.retainAll(subSeqsGeneratedCopy);
+        return ((double) subSeqsPossible.size()) / possibleSize;
+    }
+
+    /**
+     * <p>
+     * Calculates the weight of generated subsequences of length k that occur and have not been
+     * observed, with references to all possible new subsequences.
+     * </p>
+     * 
+     * @param process
+     *            stochastic process which is used to determine the weights and which subsequences
+     *            are possible
+     * @return coverage percentage
+     * @throws IllegalArgumentException
+     *             thrown if process is null
+     */
+    public double getCoveragePossibleNewWeight(IStochasticProcess process) {
+        if (process == null) {
+            throw new IllegalArgumentException("process must not be null");
+        }
+        createSubSeqs();
+        Collection<List<Event>> subSeqsGeneratedCopy =
+            new LinkedHashSet<List<Event>>(subSeqsGenerated);
+        Collection<List<Event>> subSeqsPossible = process.generateSequences(length);
+        subSeqsGeneratedCopy.removeAll(subSeqsObserved);
+        subSeqsPossible.removeAll(subSeqsObserved);
+        Map<List<Event>, Double> weightMap =
+            SequenceTools.generateWeights(process, subSeqsPossible);
+        double weight = 0.0d;
+        for (List<Event> subSeq : subSeqsGeneratedCopy) {
+            Double currentWeight = weightMap.get(subSeq);
+            if (currentWeight != null) {
+                weight += currentWeight;
+            }
+        }
+        return weight;
+    }
+
+    /**
+     * <p>
+     * Returns the number of covered subsequences of length k.
+     * </p>
+     * 
+     * @return number of covered subsequences
+     */
+    public int getNumObserved() {
+        createSubSeqs();
+        return subSeqsObserved.size();
+    }
+
+    /**
+     * <p>
+     * Returns the number of covered subsequences of length k.
+     * </p>
+     * 
+     * @return number of covered subsequences
+     */
+    public int getNumCovered() {
+        createSubSeqs();
+        return subSeqsGenerated.size();
+    }
+
+    public int getNumNew() {
+        createSubSeqs();
+        Collection<List<Event>> subSeqsGeneratedCopy =
+            new LinkedHashSet<List<Event>>(subSeqsGenerated);
+        subSeqsGeneratedCopy.removeAll(subSeqsObserved);
+        return subSeqsGeneratedCopy.size();
+    }
+
+    /**
+     * <p>
+     * Helper function that calcuates the subsequences of length k that have been observed and
+     * generated.
+     * </p>
+     */
+    private void createSubSeqs() {
+        if (subSeqsObserved == null) {
+            subSeqsObserved = SequenceTools.containedSubSequences(observedSequences, length);
+        }
+        if (subSeqsGenerated == null) {
+            subSeqsGenerated = SequenceTools.containedSubSequences(sequences, length);
+        }
+    }
+}
Index: trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorProcess.java
===================================================================
--- trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorProcess.java	(revision 922)
+++ trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/CoverageCalculatorProcess.java	(revision 922)
@@ -0,0 +1,203 @@
+package de.ugoe.cs.autoquest.coverage;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+
+/**
+ * <p>
+ * This class calculates various types of sequence coverage in relation to a stochastic process.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CoverageCalculatorProcess {
+
+    /**
+     * <p>
+     * Stochastic process that is the foundation for probabilistic coverages and coverages with
+     * reference to all possible sequences.
+     * </p>
+     */
+    private final IStochasticProcess process;
+
+    /**
+     * <p>
+     * Sequences for which the coverage is calculated.
+     * </p>
+     */
+    private Collection<List<Event>> sequences;
+
+    /**
+     * <p>
+     * Length of the subsequences in relation to which the coverage is calculated.
+     * </p>
+     */
+    private final int length;
+
+    /**
+     * <p>
+     * All subsequences of {@link #length} of {@link #sequences}.
+     * </p>
+     */
+    private Collection<List<Event>> containedSubSeqs = null;
+
+    /**
+     * <p>
+     * All subsequences of {@link #length} that can be generated by {@link #process}.
+     * </p>
+     */
+    private Collection<List<Event>> allPossibleSubSeqs = null;
+
+    /**
+     * <p>
+     * The probabilities of all subsequences of {@link #length} according to {@link #process}.
+     * </p>
+     */
+    private Map<List<Event>, Double> subSeqWeights = null;
+
+    /**
+     * <p>
+     * Constructor. Creates a new CoverageCalculatorProcess for a given stochastic process and
+     * generated sequences.
+     * </p>
+     * 
+     * @param process
+     *            stochastic process used for coverage calculations; must not be null
+     * @param sequences
+     *            sequences for which the coverage is calculated; must not be null
+     * @param length
+     *            length of the subsequences for which the coverage is analyzed; must be >0
+     * @throws IllegalArgumentException
+     *             thrown if process or sequences is null or length less than or equal to 0
+     */
+    public CoverageCalculatorProcess(IStochasticProcess process,
+                                     Collection<List<Event>> sequences,
+                                     int length)
+    {
+        if (process == null) {
+            throw new IllegalArgumentException("process must not be null");
+        }
+        if (sequences == null) {
+            throw new IllegalArgumentException("sequences must not be null");
+        }
+        if (length <= 0) {
+            throw new IllegalArgumentException("length must be >0; actual value: " + length);
+        }
+        this.process = process;
+        this.sequences = sequences;
+        this.length = length;
+    }
+
+    /**
+     * <p>
+     * Calculates the percentage of subsequences of length k that occur, including those that cannot
+     * be generated by {@link #process}.
+     * </p>
+     * 
+     * @return coverage percentage
+     */
+    public double getCoverageAllNoWeight() {
+        if (containedSubSeqs == null) {
+            containedSubSeqs = SequenceTools.containedSubSequences(sequences, length);
+        }
+        return ((double) containedSubSeqs.size()) / SequenceTools.numSequences(process, length);
+    }
+
+    /**
+     * <p>
+     * Calculates the percentage of subsequences of length k that occur and can generated by
+     * {@link #process}.
+     * </p>
+     * 
+     * @return coverage percentage
+     */
+    public double getCoveragePossibleNoWeight() {
+        if (containedSubSeqs == null) {
+            containedSubSeqs = SequenceTools.containedSubSequences(sequences, length);
+        }
+        if (allPossibleSubSeqs == null) {
+            allPossibleSubSeqs = process.generateSequences(length);
+        }
+        return ((double) containedSubSeqs.size()) / allPossibleSubSeqs.size();
+    }
+
+    /**
+     * <p>
+     * Calculates the weight of the subsequences that occur with relation to {@link #process}, i.e.,
+     * the mass of the subsequence probability covered by the subsequences.
+     * </p>
+     * 
+     * @return coverage weight
+     */
+    public double getCoveragePossibleWeight() {
+        if (containedSubSeqs == null) {
+            containedSubSeqs = SequenceTools.containedSubSequences(sequences, length);
+        }
+        if (allPossibleSubSeqs == null) {
+            allPossibleSubSeqs = process.generateSequences(length);
+        }
+        if (subSeqWeights == null) {
+            subSeqWeights = SequenceTools.generateWeights(process, allPossibleSubSeqs);
+        }
+        double weight = 0.0;
+        for (List<Event> subSeq : containedSubSeqs) {
+            Double curWeight = subSeqWeights.get(subSeq);
+            if (curWeight != null) {
+                weight += curWeight;
+            }
+        }
+        return weight;
+    }
+
+    /**
+     * <p>
+     * Returns the number of covered subsequences of length k.
+     * </p>
+     * 
+     * @return number of covered subsequences
+     */
+    public int getNumCovered() {
+        if (containedSubSeqs == null) {
+            containedSubSeqs = SequenceTools.containedSubSequences(sequences, length);
+        }
+        return containedSubSeqs.size();
+    }
+
+    /**
+     * <p>
+     * Returns the number of possible subsequences of length k according to the stochastic process.
+     * </p>
+     * 
+     * @return number of possible subsequences
+     */
+    public int getNumPossible() {
+        if (allPossibleSubSeqs == null) {
+            allPossibleSubSeqs = process.generateSequences(length);
+        }
+        return allPossibleSubSeqs.size();
+    }
+
+    /**
+     * <p>
+     * Sets a new collection of sequences for which the coverage is analyzed.
+     * </p>
+     * 
+     * @param newSequences
+     *            new collection of sequences
+     * @throws IllegalArgumentException
+     *             thrown is newSequences is null
+     */
+    public void setSequences(Collection<List<Event>> newSequences) {
+        if (newSequences == null) {
+            throw new IllegalArgumentException("sequences must not be null");
+        }
+        this.sequences = newSequences;
+        containedSubSeqs = null;
+    }
+
+}
Index: trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/SequenceTools.java
===================================================================
--- trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/SequenceTools.java	(revision 922)
+++ trunk/autoquest-core-coverage/src/main/java/de/ugoe/cs/autoquest/coverage/SequenceTools.java	(revision 922)
@@ -0,0 +1,143 @@
+package de.ugoe.cs.autoquest.coverage;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+
+/**
+ * <p>
+ * This class gathers some helper functions to work with sequences.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class SequenceTools {
+
+    /**
+     * <p>
+     * Private constructor to prevent initializing of the class.
+     * </p>
+     */
+    private SequenceTools() {
+
+    }
+
+    /**
+     * <p>
+     * Calculates the weights for all sequences passed to this function as defined by the stochastic
+     * process and stores them in a {@link Map}. The weights are normalized, i.e., the sum of all
+     * weights contained in this map is 1.
+     * </p>
+     * 
+     * @param process
+     *            process used for weight calculation
+     * @param sequences
+     *            sequences for which the weights are calculated
+     * @return {@link Map} of weights
+     */
+    public static Map<List<Event>, Double> generateWeights(IStochasticProcess process,
+                                                           Collection<List<Event>> sequences)
+    {
+        Map<List<Event>, Double> subSeqWeights = new LinkedHashMap<List<Event>, Double>();
+        if (sequences != null && !sequences.isEmpty()) {
+            if (process != null) {
+                double sum = 0.0;
+                for (List<Event> sequence : sequences) {
+                    double prob = process.getProbability(sequence);
+                    subSeqWeights.put(sequence, prob);
+                    sum += prob;
+                }
+                if (sum < 1.0) {
+                    for (Map.Entry<List<Event>, Double> entry : subSeqWeights.entrySet()) {
+                        entry.setValue(entry.getValue() / sum);
+                    }
+                }
+            }
+            else {
+                for (List<Event> sequence : sequences) {
+                    subSeqWeights.put(sequence, 0.0d);
+                }
+            }
+        }
+        return subSeqWeights;
+    }
+
+    /**
+     * <p>
+     * Calculates the number of all existing sequences of a given length, regardless whether they
+     * are possible or not.
+     * </p>
+     * 
+     * @param process
+     *            stochastic process whose symbols are the basis for this calculation
+     * @param length
+     *            lenght of the sequences
+     * @return numStates^length
+     * @throws IllegalArgumentException
+     *             thrown if length less or equal to 0
+     */
+    public static long numSequences(IStochasticProcess process, int length) {
+        if (length <= 0) {
+            throw new IllegalArgumentException("length must be a positive integer");
+        }
+        long result = 0;
+        if (process != null) {
+            result = (long) Math.pow(process.getNumSymbols(), length);
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Creates a {@link Set} of all subsequences of a given length that are contained in a sequence
+     * collection.
+     * </p>
+     * 
+     * @param sequences
+     *            sequences from which the subsequences are extracted
+     * @param length
+     *            length of the subsequences
+     * @return {@link Set} of all subsequences
+     * @throws IllegalArgumentException
+     *             thrown if length less or equal to 0
+     */
+    public static Set<List<Event>> containedSubSequences(Collection<List<Event>> sequences,
+                                                         int length)
+    {
+        if (length <= 0) {
+            throw new IllegalArgumentException("length must be a positive integer");
+        }
+        Set<List<Event>> containedSubSeqs = new LinkedHashSet<List<Event>>();
+        if (sequences != null) {
+            for (List<Event> sequence : sequences) {
+                List<Event> subSeq = new LinkedList<Event>();
+                boolean minLengthReached = false;
+                for (Event event : sequence) {
+                    subSeq.add(event);
+                    if (!minLengthReached) {
+                        if (subSeq.size() == length) {
+                            minLengthReached = true;
+                        }
+                    }
+                    else {
+                        subSeq.remove(0);
+                    }
+                    if (minLengthReached) {
+                        if (!containedSubSeqs.contains(subSeq)) {
+                            containedSubSeqs.add(new LinkedList<Event>(subSeq));
+                        }
+                    }
+                }
+            }
+        }
+        return containedSubSeqs;
+    }
+}
Index: trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/SequenceInstanceOfTest.java
===================================================================
--- trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/SequenceInstanceOfTest.java	(revision 922)
+++ trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/SequenceInstanceOfTest.java	(revision 922)
@@ -0,0 +1,102 @@
+package de.ugoe.cs.autoquest;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.*;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+
+/**
+ * The class <code>SequenceInstanceOfTest</code> contains tests for the
+ * class <code>{@link SequenceInstanceOf}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class SequenceInstanceOfTest {
+	
+	@Test
+	public void TestIsCollectionOfSequences_1() throws Exception {
+		Collection<List<Event>> sequences = new LinkedList<List<Event>>();
+		List<Event> sequence1 = new ArrayList<Event>();
+		sequence1.add(new Event(mock(IEventType.class)));
+		sequences.add(sequence1);
+		
+		boolean result = SequenceInstanceOf.isCollectionOfSequences(sequences);
+		assertTrue(result);
+	}
+	
+	@Test
+	public void TestIsCollectionOfSequences_2() throws Exception {
+		Collection<List<Event>> sequences = new LinkedList<List<Event>>();
+		List<Event> sequence1 = new ArrayList<Event>();
+		sequences.add(sequence1);
+		
+		boolean result = SequenceInstanceOf.isCollectionOfSequences(sequences);
+		assertFalse(result);
+	}
+	
+	@Test
+	public void TestIsCollectionOfSequences_3() throws Exception {
+		Collection<List<Event>> sequences = new LinkedList<List<Event>>();
+		
+		boolean result = SequenceInstanceOf.isCollectionOfSequences(sequences);
+		assertFalse(result);
+	}
+	
+	@Test
+	public void TestIsCollectionOfSequences_4() throws Exception {
+		boolean result = SequenceInstanceOf.isCollectionOfSequences(null);
+		assertFalse(result);
+	}
+	
+	@Test
+	public void TestIsCollectionOfSequences_5() throws Exception {
+		boolean result = SequenceInstanceOf.isCollectionOfSequences(new Object());
+		assertFalse(result);
+	}
+	
+	@Test
+	public void TestIsEventSequence_1() throws Exception {
+		List<Event> sequence = new ArrayList<Event>();
+		sequence.add(new Event(mock(IEventType.class)));
+		
+		boolean result = SequenceInstanceOf.isEventSequence(sequence);
+		assertTrue(result);
+	}
+	
+	@Test
+	public void TestIsEventSequence_2() throws Exception {
+		List<Event> sequence = new ArrayList<Event>();
+		
+		boolean result = SequenceInstanceOf.isEventSequence(sequence);
+		assertFalse(result);
+	}
+	
+	@Test
+	public void TestIsEventSequence_3() throws Exception {
+		boolean result = SequenceInstanceOf.isEventSequence(null);
+		assertFalse(result);
+	}
+	
+	@Test
+	public void TestIsEventSequence_4() throws Exception {
+		boolean result = SequenceInstanceOf.isEventSequence(new Object());
+		assertFalse(result);
+	}
+	
+	
+	
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(SequenceInstanceOfTest.class);
+	}
+}
Index: trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/EventTest.java
===================================================================
--- trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/EventTest.java	(revision 922)
+++ trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/EventTest.java	(revision 922)
@@ -0,0 +1,304 @@
+package de.ugoe.cs.autoquest.eventcore;
+
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.eventcore.IReplayable;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * The class <code>EventTest</code> contains tests for the class <code>{@link Event}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class EventTest {
+
+    @Test
+    public void testEvent_1() throws Exception {
+        Event result = new Event(mock(IEventType.class));
+
+        assertNotNull(result);
+    }
+
+    @Test(expected = java.lang.IllegalArgumentException.class)
+    public void testEvent_2() throws Exception {
+        new Event(null);
+    }
+
+    /*
+    @Test
+    public void testEquals_1() throws Exception {
+        IEventType type1 = mock(IEventType.class);
+        IEventType type2 = mock(IEventType.class);
+        // when(type1.equals(type2)).thenReturn(true);
+        Event fixture = new Event(type1);
+        Event other = new Event(type2);
+
+        boolean result = fixture.equals(other);
+
+        assertTrue(result);
+    }
+
+    @Test
+    public void testEquals_2() throws Exception {
+        String type1 = "typeString1";
+        String type2 = "typeString2";
+        Event fixture = new Event(type1);
+        Event other = new Event(type2);
+
+        boolean result = fixture.equals(other);
+
+        assertFalse(result);
+    }
+
+    @Test
+    public void testEquals_3() throws Exception {
+        String type1 = "typeString";
+        String type2 = "typeString";
+        String target1 = "target";
+        String target2 = "target";
+        Event fixture = new Event(type1);
+        fixture.target = target1;
+        Event other = new Event(type2);
+        other.target = target2;
+
+        boolean result = fixture.equals(other);
+
+        assertTrue(result);
+    }
+
+    @Test
+    public void testEquals_4() throws Exception {
+        String type1 = "typeString1";
+        String type2 = "typeString2";
+        String target1 = "target";
+        String target2 = "target";
+        Event fixture = new Event(type1);
+        fixture.target = target1;
+        Event other = new Event(type2);
+        other.target = target2;
+
+        boolean result = fixture.equals(other);
+
+        assertFalse(result);
+    }
+
+    @Test
+    public void testEquals_5() throws Exception {
+        String type1 = "typeString";
+        String type2 = "typeString";
+        String target1 = "target1";
+        String target2 = "target2";
+        Event fixture = new Event(type1);
+        fixture.target = target1;
+        Event other = new Event(type2);
+        other.target = target2;
+
+        boolean result = fixture.equals(other);
+
+        assertFalse(result);
+    }
+
+    @Test
+    public void testEquals_6() throws Exception {
+        String type = "typeString";
+        Event fixture = new Event(type);
+
+        boolean result = fixture.equals(fixture);
+
+        assertTrue(result);
+    }
+
+    @Test
+    public void testEqualsContract() throws Exception {
+        EqualsVerifier.forClass(Event.class).suppress(Warning.NONFINAL_FIELDS)
+            .withRedefinedSubclass(ReplayableEvent.class).verify();
+    }
+    */
+
+    @Test
+    public void testGetTarget_1() throws Exception {
+        IEventType type = mock(IEventType.class);
+        IEventTarget target = mock(IEventTarget.class);
+        
+        Event fixture = new Event(type);
+        fixture.setTarget(target);
+
+        IEventTarget result = fixture.getTarget();
+
+        assertSame(target, result);
+    }
+    
+    
+
+    @Test
+    public void testGetTarget_2() throws Exception {
+        IEventType type = mock(IEventType.class);
+        IEventTarget target = mock(IEventTarget.class);
+        
+        Event fixture = new Event(type, target);
+
+        IEventTarget result = fixture.getTarget();
+
+        assertSame(target, result);
+    }
+    
+    @Test
+    public void testGetTarget_3() throws Exception {
+        IEventType type = mock(IEventType.class);
+        
+        Event fixture = new Event(type);
+
+        IEventTarget result = fixture.getTarget();
+
+        assertNull(result);
+    }
+
+    @Test
+    public void testGetType_1() throws Exception {
+        IEventType type = mock(IEventType.class);
+        
+        Event fixture = new Event(type);
+
+        IEventType result = fixture.getType();
+
+        assertEquals(type, result);
+    }
+
+    @Test
+    public void testSetTarget_1() throws Exception {
+        IEventType type = mock(IEventType.class);
+        IEventTarget target = mock(IEventTarget.class);
+        
+        Event fixture = new Event(type);
+
+        boolean result = fixture.setTarget(target);
+
+        assertTrue(result);
+        assertEquals(target, fixture.getTarget());
+    }
+
+    @Test
+    public void testSetTarget_2() throws Exception {
+        IEventType type = mock(IEventType.class);
+        IEventTarget target1 = mock(IEventTarget.class);
+        IEventTarget target2 = mock(IEventTarget.class);
+        
+        Event fixture = new Event(type);
+
+        fixture.setTarget(target1);
+        boolean result = fixture.setTarget(target2);
+        
+        assertFalse(result);
+        assertEquals(target1, fixture.target);
+    }
+    
+    @Test
+    public void testSetTarget_3() throws Exception {
+        IEventType type = mock(IEventType.class);
+        IEventTarget target1 = mock(IEventTarget.class);
+        IEventTarget target2 = mock(IEventTarget.class);
+        
+        Event fixture = new Event(type, target1);
+
+        boolean result = fixture.setTarget(target2);
+        
+        assertFalse(result);
+        assertEquals(target1, fixture.target);
+    }
+
+    @Test
+    public void testGetId_1() throws Exception {
+        IEventType type = mock(IEventType.class);
+        when(type.toString()).thenReturn("typeString");
+        IEventTarget target = mock(IEventTarget.class);
+        when(target.getStringIdentifier()).thenReturn("targetString");
+        
+        Event fixture = new Event(type, target);
+
+        String result = fixture.toString();
+
+        assertEquals("typeString.targetString", result);
+    }
+    
+    @Test
+    public void testGetId_2() throws Exception {
+        IEventType type = mock(IEventType.class);
+        when(type.toString()).thenReturn("typeString");
+        
+        Event fixture = new Event(type);
+        
+        String result = fixture.toString();
+        
+        assertEquals("typeString", result);
+    }
+    
+    @Test
+    public void testAddReplayable_1() throws Exception {
+        IReplayable replayable = mock(IReplayable.class);
+
+        Event fixture = new Event(mock(IEventType.class));
+        fixture.addReplayable(replayable);
+
+        assertEquals(1, fixture.getReplayables().size());
+        assertEquals(replayable, fixture.getReplayables().get(0));
+    }
+
+    @Test
+    public void testAddReplayEvent_2() throws Exception {
+        IReplayable replayable1 = mock(IReplayable.class);
+        IReplayable replayable2 = mock(IReplayable.class);
+
+        Event fixture = new Event(mock(IEventType.class));
+        fixture.addReplayable(replayable1);
+        fixture.addReplayable(replayable2);
+
+        assertEquals(2, fixture.getReplayables().size());
+        assertEquals(replayable1, fixture.getReplayables().get(0));
+        assertEquals(replayable2, fixture.getReplayables().get(1));
+    }
+
+    @Test(expected = java.lang.IllegalArgumentException.class)
+    public void testAddReplayEvent_fixture_3() throws Exception {
+        Event fixture = new Event(mock(IEventType.class));
+        fixture.addReplayable(null);
+    }
+
+    @Test
+    public void testAddReplaySequence_1() throws Exception {
+        IReplayable replayable1 = mock(IReplayable.class);
+        IReplayable replayable2 = mock(IReplayable.class);
+
+        List<IReplayable> replayableSequences = new LinkedList<IReplayable>();
+        replayableSequences.add(replayable1);
+        replayableSequences.add(replayable2);
+
+        Event fixture = new Event(mock(IEventType.class));
+
+        fixture.addReplayableSequence(replayableSequences);
+
+        assertEquals(2, fixture.getReplayables().size());
+        assertEquals(replayable1, fixture.getReplayables().get(0));
+        assertEquals(replayable2, fixture.getReplayables().get(1));
+    }
+
+    @Test(expected = java.lang.IllegalArgumentException.class)
+    public void testAddReplaySequence_2() throws Exception {
+        Event fixture = new Event(mock(IEventType.class));
+
+        fixture.addReplayableSequence(null);
+    }
+
+    public static void main(String[] args) {
+        new org.junit.runner.JUnitCore().run(EventTest.class);
+    }
+}
Index: trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionCorrectorTest.java
===================================================================
--- trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionCorrectorTest.java	(revision 922)
+++ trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionCorrectorTest.java	(revision 922)
@@ -0,0 +1,440 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.mockito.Mockito.*;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteractionCorrector;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyTyped;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteractionCorrector.CleanupMode;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction.Button;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * @author Patrick Harms, Steffen Herbold
+ */
+public class KeyInteractionCorrectorTest {
+
+    @Test
+    public void testSortKeyInteractions_1() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // Normal pressing and releasing of A and B
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_2() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // A is pressed and not released before B is pressed
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_3() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // SHIFT is pressed and released after all keys are pressed
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_4() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // SHIFT is released before the last key is released
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+
+        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_5() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // SHIFT is released before all other keys are released
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_C), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_C), guiElement));
+
+        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_C), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_6() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // SHIFT, CTRL and ALT are pressed and released after all keys are pressed
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        expected.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
+        expected.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_7() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // now SHIFT, CTRL and ALT are released before the last key is released
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+
+        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        expected.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
+        expected.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_8() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // SHIFT, CTRL and ALT are released in another order and before some other keys are
+        // released
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_C), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_C), guiElement));
+
+        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        expected.add(new Event(new KeyPressed(VirtualKey.CONTROL), guiElement));
+        expected.add(new Event(new KeyPressed(VirtualKey.ALT), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_C), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.ALT), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.CONTROL), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_9() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // holding of A
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_10() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // holding of shift and meanwhile pressing of A
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        expected.add(new Event(new KeyPressed(VirtualKey.SHIFT), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyReleased(VirtualKey.SHIFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_11() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // Check if keyboard focus change is moved in front of a sequence in case it is in between a
+        // pressed
+        // released pair
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyboardFocusChange(), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+
+        expected.add(new Event(new KeyboardFocusChange(), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_12() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector();
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // Check if mouse events stay where they are
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new MouseButtonDown(Button.LEFT), guiElement));
+        input.add(new Event(new MouseButtonUp(Button.LEFT), guiElement));
+        input.add(new Event(new MouseClick(Button.LEFT), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new MouseButtonDown(Button.LEFT), guiElement));
+        expected.add(new Event(new MouseButtonUp(Button.LEFT), guiElement));
+        expected.add(new Event(new MouseClick(Button.LEFT), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_13() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector(CleanupMode.REMOVAL);
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // Check if mouse events stay where they are
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @Test
+    public void testSortKeyInteractions_14() {
+        List<Event> input = new LinkedList<Event>();
+        List<Event> expected = new LinkedList<Event>();
+
+        KeyInteractionCorrector sorter = new KeyInteractionCorrector(CleanupMode.ADDITION);
+
+        IGUIElement guiElement = mock(IGUIElement.class);
+
+        // Check if mouse events stay where they are
+        input.add(new Event(new KeyPressed(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_A), guiElement));
+        input.add(new Event(new KeyReleased(VirtualKey.LETTER_B), guiElement));
+
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_A), guiElement));
+        expected.add(new Event(new KeyTyped(VirtualKey.LETTER_B), guiElement));
+
+        List<Event> output = sorter.sortKeyInteractions(input);
+
+        assertEquals(expected, output);
+    }
+
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        Console.reset();
+        Console.getInstance().registerTraceListener(new TextConsole(Level.INFO));
+    }
+}
Index: trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClickCondenserTest.java
===================================================================
--- trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClickCondenserTest.java	(revision 922)
+++ trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClickCondenserTest.java	(revision 922)
@@ -0,0 +1,110 @@
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClickCondenser;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+
+/**
+ * @author Patrick Harms
+ */
+public class MouseClickCondenserTest {
+
+    /** */
+    private List<Event> events = new ArrayList<Event>();
+
+    /**
+     *
+     */
+    @Test
+    public void testMouseClickInOneSequenceElement() {
+        MouseButtonInteraction.Button leftBtn = MouseButtonInteraction.Button.LEFT;
+        MouseButtonInteraction.Button middleBtn = MouseButtonInteraction.Button.MIDDLE;
+        MouseButtonInteraction.Button rightBtn = MouseButtonInteraction.Button.RIGHT;
+
+        IGUIElement element1 = new DummyGUIElement("elem1");
+        IGUIElement element2 = new DummyGUIElement("elem2");
+
+        simulateEvent(new MouseButtonDown(leftBtn), element1);
+        simulateEvent(new MouseButtonUp(leftBtn), element1);
+        simulateEvent(new MouseClick(leftBtn), element1);
+        assertCondensedMouseClicks("elem1");
+
+        simulateEvent(new DummyInteraction("bla", 1), element1);
+        simulateEvent(new DummyInteraction("bli", 1), element1);
+        simulateEvent(new MouseButtonDown(middleBtn), element1);
+        simulateEvent(new MouseButtonUp(middleBtn), element1);
+        simulateEvent(new MouseClick(middleBtn), element1);
+        simulateEvent(new DummyInteraction("blo", 1), element1);
+        simulateEvent(new DummyInteraction("blu", 1), element1);
+        assertCondensedMouseClicks("elem1", "", "", "elem1", "", "");
+
+        simulateEvent(new DummyInteraction("bla", 1), element2);
+        simulateEvent(new DummyInteraction("bli", 1), element2);
+        simulateEvent(new MouseButtonDown(rightBtn), element2);
+        simulateEvent(new MouseButtonUp(rightBtn), element2);
+        simulateEvent(new MouseClick(rightBtn), element2);
+        simulateEvent(new DummyInteraction("blo", 1), element2);
+        simulateEvent(new DummyInteraction("blu", 1), element2);
+        assertCondensedMouseClicks("elem1", "", "", "elem1", "", "", "", "", "elem2", "", "");
+
+        simulateEvent(new MouseButtonDown(leftBtn), element1);
+        simulateEvent(new MouseButtonUp(leftBtn), element1);
+        simulateEvent(new MouseClick(leftBtn), element2);
+        assertCondensedMouseClicks("elem1", "", "", "elem1", "", "", "", "", "elem2", "", "",
+                                   "", "", "elem2");
+
+        simulateEvent(new MouseButtonDown(middleBtn), element1);
+        simulateEvent(new MouseButtonUp(middleBtn), element1);
+        simulateEvent(new MouseClick(rightBtn), element1);
+        simulateEvent(new DummyInteraction("bla", 1), element2);
+        assertCondensedMouseClicks("elem1", "", "", "elem1", "", "", "", "", "elem2", "", "",
+                                   "", "", "elem2", "", "", "elem1", "");
+    }
+
+    /**
+     *
+     */
+    private void simulateEvent(IEventType eventType, IGUIElement guiElement) {
+        events.add(new Event(eventType, guiElement));
+    }
+
+    /**
+    *
+    */
+   private void assertCondensedMouseClicks(String... clickedTargets) {
+       MouseClickCondenser condenser = new MouseClickCondenser();
+       List<Event> result = condenser.condenseMouseClicks(events);
+       
+       assertEquals(clickedTargets.length, result.size());
+       
+       for (int i = 0; i < clickedTargets.length; i++) {
+           String clickedTarget = clickedTargets[i];
+           
+           if ((clickedTarget != null) && (!"".equals(clickedTarget))) {
+               assertTrue(result.get(i).getType() instanceof MouseClick);
+               assertTrue(result.get(i).getTarget() instanceof DummyGUIElement);
+               assertEquals
+                 (clickedTarget, ((DummyGUIElement) result.get(i).getTarget()).getStringIdentifier());
+           }
+           else {
+               assertFalse(result.get(i).getType() instanceof MouseClick);
+           }
+       }
+   }
+}
Index: trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/TextInputDetectorTest.java
===================================================================
--- trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/TextInputDetectorTest.java	(revision 922)
+++ trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/gui/TextInputDetectorTest.java	(revision 922)
@@ -0,0 +1,222 @@
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInputDetector;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyTextField;
+
+/**
+ * @author Patrick Harms
+ */
+public class TextInputDetectorTest {
+
+    /** */
+    private List<Event> events = new ArrayList<Event>();
+
+    /**
+     *
+     */
+    @Test
+    public void testSimpleTextEntry() {
+        IGUIElement element1 = new DummyTextField("elem1");
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
+        assertTextInput("a");
+
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
+        assertTextInput("ab");
+
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
+        assertTextInput("abC");
+
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_D), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_D), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_E), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_E), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_F), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_F), element1);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
+        assertTextInput("abCDEF");
+
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_G), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_G), element1);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
+        assertTextInput("abCDEFg");
+
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testTextEntryOnDifferentGuiElements() {
+        IGUIElement element1 = new DummyTextField("elem1");
+        IGUIElement element2 = new DummyTextField("elem2");
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
+        assertTextInput("a");
+
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element2);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element2);
+        assertTextInput("a", "b");
+
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
+        assertTextInput("a", "b", "C");
+
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element2);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_D), element2);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_D), element2);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_E), element2);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_E), element2);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_F), element2);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_F), element2);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element2);
+        assertTextInput("a", "b", "C", "DEF");
+
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyPressed(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_G), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_G), element1);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
+        simulateEvent(new KeyReleased(VirtualKey.SHIFT), element1);
+        assertTextInput("a", "b", "C", "DEF", "g");
+
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testUsageOfBackspace() {
+        IGUIElement element1 = new DummyTextField("elem1");
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_D), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_D), element1);
+        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
+        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_E), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_E), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_F), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_F), element1);
+        assertTextInput("abcef");
+
+        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
+        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
+        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
+        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
+        simulateEvent(new KeyPressed(VirtualKey.BACK_SPACE), element1);
+        simulateEvent(new KeyReleased(VirtualKey.BACK_SPACE), element1);
+        assertTextInput("ab");
+
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testOtherInteractionsInBetween() {
+        IGUIElement element1 = new DummyTextField("elem1");
+        IGUIElement element2 = new DummyGUIElement("elem2");
+        IGUIElement element3 = new DummyGUIElement("elem3");
+        
+        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element2);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
+        assertTextInput("", "abc");
+
+        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element2);
+        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
+        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
+        assertTextInput("", "abc", "", "", "abc", "");
+
+        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element2);
+        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_A), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_B), element1);
+        simulateEvent(new KeyPressed(VirtualKey.LETTER_C), element1);
+        simulateEvent(new MouseClick(MouseClick.Button.LEFT), element3);
+        simulateEvent(new KeyReleased(VirtualKey.LETTER_C), element1);
+        
+        // the pressing of the c is not completed. So it must be included in the resulting
+        // text input event but it may only be followed by the mouse click. The subsequent release
+        // of the c key is ignored and results in a removal of the event.
+        assertTextInput("", "abc", "", "", "abc", "", "", "", "abc", "");
+    }
+
+    /**
+     *
+     */
+    private void simulateEvent(IEventType eventType, IGUIElement guiElement) {
+        events.add(new Event(eventType, guiElement));
+    }
+
+    /**
+     *
+     */
+    private void assertTextInput(String... enteredTexts) {
+        TextInputDetector detector = new TextInputDetector();
+        List<Event> result = detector.detectTextInputs(events);
+        
+        assertEquals(enteredTexts.length, result.size());
+        
+        for (int i = 0; i < enteredTexts.length; i++) {
+            String enteredText = enteredTexts[i];
+            
+            if ((enteredText != null) && (!"".equals(enteredText))) {
+                assertTrue(result.get(i).getType() instanceof TextInput);
+                assertNotNull(((TextInput) result.get(i).getType()).getEnteredText());
+                assertEquals(enteredText, ((TextInput) result.get(i).getType()).getEnteredText());
+            
+                assertNotNull(((TextInput) result.get(i).getType()).getTextInputEvents());
+                assertTrue(((TextInput) result.get(i).getType()).getTextInputEvents().size() > 0);
+                //assertTrue
+                //    ((((TextInput) result.get(i).getType()).getTextInputEvents().size() % 2) == 0);
+            }
+            else {
+                assertFalse(result.get(i).getType() instanceof TextInput);
+            }
+        }
+    }
+}
Index: trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/guimodel/AbstractDefaultGUIElementTest.java
===================================================================
--- trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/guimodel/AbstractDefaultGUIElementTest.java	(revision 922)
+++ trunk/autoquest-core-events-test/src/test/java/de/ugoe/cs/autoquest/eventcore/guimodel/AbstractDefaultGUIElementTest.java	(revision 922)
@@ -0,0 +1,136 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.AbstractDefaultGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 24.08.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class AbstractDefaultGUIElementTest {
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     */
+    @Test
+    public void testRegisteringEqualGuiElements() {
+        IGUIElement guiElement1 = new AbstractDefaultGUIElementTestImpl();
+        IGUIElement guiElement2 = new AbstractDefaultGUIElementTestImpl();
+        
+        assertCompletelyUnequal(guiElement1, guiElement2);
+        
+        guiElement1.addEqualGUIElement(guiElement2);
+        assertCompletelyEqual(guiElement1, guiElement2);
+        
+        IGUIElement guiElement3 = new AbstractDefaultGUIElementTestImpl();
+        
+        assertCompletelyEqual(guiElement1, guiElement2);
+        assertCompletelyUnequal(guiElement1, guiElement3);
+        assertCompletelyUnequal(guiElement2, guiElement3);
+        
+        guiElement1.addEqualGUIElement(guiElement3);
+        assertCompletelyEqual(guiElement1, guiElement2, guiElement3);
+
+        IGUIElement guiElement4 = new AbstractDefaultGUIElementTestImpl();
+        
+        assertCompletelyEqual(guiElement1, guiElement2, guiElement3);
+        assertCompletelyUnequal(guiElement1, guiElement4);
+        assertCompletelyUnequal(guiElement2, guiElement4);
+        assertCompletelyUnequal(guiElement3, guiElement4);
+        
+        guiElement3.addEqualGUIElement(guiElement4);
+        assertCompletelyEqual(guiElement1, guiElement2, guiElement3, guiElement4);
+    }
+    
+    /**
+     * <p>
+     * 
+     * </p>
+     */
+    private void assertCompletelyUnequal(IGUIElement... guiElements) {
+        for (int i = 0; i < guiElements.length; i++) {
+            for (int j = i + 1; j < guiElements.length; j++) {
+                assertFalse(guiElements[i].equals(guiElements[j]));
+                assertFalse(guiElements[j].equals(guiElements[i]));
+                assertNotSame(guiElements[i].hashCode(), guiElements[j].hashCode());
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * 
+     * </p>
+     */
+    private void assertCompletelyEqual(IGUIElement... guiElements) {
+        for (int i = 0; i < guiElements.length; i++) {
+            for (int j = i; j < guiElements.length; j++) {
+                assertTrue(guiElements[i].equals(guiElements[j]));
+                assertTrue(guiElements[j].equals(guiElements[i]));
+                assertEquals(guiElements[i].hashCode(), guiElements[j].hashCode());
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * TODO comment
+     * </p>
+     * 
+     * @version $Revision: $ $Date: 24.08.2012$
+     * @author 2012, last modified by $Author: pharms$
+     */
+    public class AbstractDefaultGUIElementTestImpl extends AbstractDefaultGUIElement
+        implements IGUIElement
+    {
+
+        /**
+         * <p>
+         * TODO: comment
+         * </p>
+         *
+         * @param specification
+         * @param parent
+         */
+        public AbstractDefaultGUIElementTestImpl() {
+            super(null, null);
+        }
+
+        /**  */
+        private static final long serialVersionUID = 1L;
+
+        /* (non-Javadoc)
+         * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getPlatform()
+         */
+        @Override
+        public String getPlatform() {
+            return "TEST";
+        }
+
+        /* (non-Javadoc)
+         * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#updateSpecification(de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec)
+         */
+        @Override
+        public void updateSpecification(IGUIElementSpec furtherSpec) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getStringIdentifier() {
+            return "DUMMY";
+        }
+
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/IReplayDecorator.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/IReplayDecorator.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/IReplayDecorator.java	(revision 922)
@@ -0,0 +1,55 @@
+package de.ugoe.cs.autoquest;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * This interface defines the structure of decorators used when writing replay
+ * files.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface IReplayDecorator extends Serializable {
+
+	/**
+	 * <p>
+	 * Header of the file. Called at the beginning of the writing.
+	 * </p>
+	 * 
+	 * @return file header
+	 */
+	String getHeader();
+
+	/**
+	 * <p>
+	 * Footer of the file. Called at the end of the writing.
+	 * </p>
+	 * 
+	 * @return file footer
+	 */
+	String getFooter();
+
+	/**
+	 * <p>
+	 * Session Header. Called before each session.
+	 * </p>
+	 * 
+	 * @param sessionId
+	 *            id of the session
+	 * @return session header
+	 */
+	String getSessionHeader(int sessionId);
+
+	/**
+	 * <p>
+	 * Session Footer. Called after each session.
+	 * </p>
+	 * 
+	 * @param sessionId
+	 *            id of the session
+	 * @return session footer
+	 */
+	String getSessionFooter(int sessionId);
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/SequenceInstanceOf.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/SequenceInstanceOf.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/SequenceInstanceOf.java	(revision 922)
@@ -0,0 +1,79 @@
+package de.ugoe.cs.autoquest;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * Helper class that can be used to determine if an object is a sequence or a
+ * collection of sequences. {@code instanceof} does not work, because of
+ * the type erasure of generics.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class SequenceInstanceOf {
+	
+	/**
+	 * <p>
+	 * Private constructor to prevent initializing of the class.
+	 * </p>
+	 */
+	private SequenceInstanceOf() {
+		
+	}
+
+	/**
+	 * <p>
+	 * Checks if an object is of type {@link Collection}&lt;{@link List}&lt;
+	 * {@link Event}&lt;?&gt;&gt;&gt;.
+	 * </p>
+	 * 
+	 * @param obj
+	 *            object that is checked
+	 * @return true, if the obj is of type {@link Collection}&lt;{@link List}
+	 *         &lt; {@link Event}&lt;?&gt;&gt;&gt;; false otherwise
+	 */
+	public static boolean isCollectionOfSequences(Object obj) {
+		try {
+			if (obj instanceof Collection<?>) {
+				Object listObj = ((Collection<?>) obj).iterator().next();
+				if (listObj instanceof List<?>) {
+					if (((List<?>) listObj).iterator().next() instanceof Event) {
+						return true;
+					}
+				}
+			}
+		} catch (NoSuchElementException e) {
+		}
+		return false;
+	}
+
+	/**
+	 * <p>
+	 * Checks if an object is of type {@link List}&lt;{@link Event}
+	 * &lt;?&gt;&gt;.
+	 * </p>
+	 * 
+	 * @param obj
+	 *            object that is checked
+	 * @return true, if obj is of type {@link List}&lt;{@link Event}
+	 *         &lt;?&gt;&gt;; false otherwise
+	 */
+	public static boolean isEventSequence(Object obj) {
+		try {
+			if (obj instanceof List<?>) {
+				if (((List<?>) obj).iterator().next() instanceof Event) {
+					return true;
+				}
+			}
+		} catch (NoSuchElementException e) {
+		}
+		return false;
+	}
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/Event.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/Event.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/Event.java	(revision 922)
@@ -0,0 +1,251 @@
+package de.ugoe.cs.autoquest.eventcore;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <p>
+ * Base class for all events. An event is described by its {@link #type} and its {@link #target}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ * @param <T>
+ *            Can be used to declare that events belong to a specific platform without subclassing.
+ */
+public class Event implements Serializable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Global start event that can be used to indicate the start of a sequence.
+     * </p>
+     */
+    public static final Event STARTEVENT = new Event(new StringEventType("START"));
+
+    /**
+     * <p>
+     * Global end event that can be used to indicate the end of a sequence.
+     */
+    public static final Event ENDEVENT = new Event(new StringEventType("END"));
+
+    /**
+     * <p>
+     * Type of the event.
+     * </p>
+     */
+    protected IEventType type;
+
+    /**
+     * </p> Target of the event.
+     */
+    protected IEventTarget target = null;
+
+    /**
+     * <p>
+     * List of {@link IReplayable}s of type T that describes the replay of an event. The
+     * {@link IReplayable}s can be interpreted as <it>sub-events</it> on the platform level that
+     * make up the abstract event.
+     * </p>
+     */
+    protected List<IReplayable> replay = new LinkedList<IReplayable>();
+
+    /**
+     * <p>
+     * Constructor. Creates a new Event with a given type. The type must not be null.
+     * </p>
+     * 
+     * @param type
+     *            type of the event
+     */
+    public Event(IEventType type) {
+        if (type == null) {
+            throw new IllegalArgumentException("Event type must not be null");
+        }
+        this.type = type;
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new Event with a given type and target. The type must not be null.
+     * </p>
+     * 
+     * @param type
+     *            type of the event
+     * @param target
+     *            target of the event
+     */
+    public Event(IEventType type, IEventTarget target) {
+        this(type);
+        this.target = target;
+    }
+
+    /**
+     * <p>
+     * Two events are equal, if their {@link #type} and {@link #target} are equal.
+     * </p>
+     * <p>
+     * See {@link Object#equals(Object)} for further information.
+     * </p>
+     * 
+     * @param other
+     *            Event that is compared to this
+     * @return true, if events are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other instanceof Event) {
+            Event otherEvent = (Event) other;
+            if (target != null) {
+                return type.equals(otherEvent.type) && target.equals(otherEvent.target);
+            }
+            else {
+                return type.equals(otherEvent.type) && otherEvent.target == null;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * <p>
+     * Returns {@link #getId()} as String representation of the event.
+     * </p>
+     * 
+     * @return String representation of the event
+     */
+    @Override
+    public String toString() {
+        return getId();
+    }
+
+    /**
+     * <p>
+     * Returns the {@link #target} of the event.
+     * </p>
+     * 
+     * @return {@link #target} of the event
+     */
+    public IEventTarget getTarget() {
+        return target;
+    }
+
+    /**
+     * <p>
+     * Returns the {@link #type} of the event.
+     * </p>
+     * 
+     * @return {@link #type} of the event
+     */
+    public IEventType getType() {
+        return type;
+    }
+    
+    /**
+     * <p>
+     * Returns the combination of {@link #type} and {@link #target} as id.
+     * </p>
+     *
+     * @return string of the form (type,target)
+     */
+    public String getId() {
+        String id = type.toString();
+        if( target!=null ) {
+            id += "." + target.getStringIdentifier();
+        }
+        return id;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+        if (type != null) {
+            hash = multiplier * hash + type.hashCode();
+        }
+        if (target != null) {
+            hash = multiplier * hash + target.hashCode();
+        }
+        return hash;
+    }
+
+    /**
+     * <p>
+     * Sets the target of the event. Once set, the target cannot be changed.
+     * </p>
+     * 
+     * @param target
+     *            target of the event
+     * @return true, if target was changed, false otherwise
+     */
+    public boolean setTarget(IEventTarget target) {
+        if (this.target != null) {
+            return false;
+        }
+        this.target = target;
+        return true;
+    }
+
+    /**
+     * <p>
+     * Adds a new {@link IReplayable} of type T to the replay sequence.
+     * </p>
+     * 
+     * @param replayable
+     *            element that is added to the sequence
+     * @throws IllegalArgumentException
+     *             thrown is replayable is null
+     */
+    public void addReplayable(IReplayable replayable) {
+        if (replayable == null) {
+            throw new IllegalArgumentException("replayble must not be null");
+        }
+        replay.add(replayable);
+    }
+
+    /**
+     * <p>
+     * Adds a {@link List}ist of {@link IReplayable} to the replay sequence.
+     * </p>
+     * 
+     * @param generatedReplaySeq
+     *            {@link List} that is added to the sequence
+     * @throws IllegalArgumentException
+     *             thrown if generatedReplaySeq is null
+     */
+    public void addReplayableSequence(List<? extends IReplayable> generatedReplaySeq) {
+        if (generatedReplaySeq == null) {
+            throw new IllegalArgumentException("generatedReplaySeq must not be null");
+        }
+        replay.addAll(generatedReplaySeq);
+    }
+
+    /**
+     * <p>
+     * Returns a the list of replay events.
+     * </p>
+     * <p>
+     * The return value is a copy of the list used internally!
+     * </p>
+     * 
+     * @return list of replay events.
+     */
+    public List<IReplayable> getReplayables() {
+        return new LinkedList<IReplayable>(replay);
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IEventTarget.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IEventTarget.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IEventTarget.java	(revision 922)
@@ -0,0 +1,36 @@
+
+package de.ugoe.cs.autoquest.eventcore;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * Common interface for event targets. An event target can, e.g., be an element of a GUI or Web
+ * server. A concrete event-driven software platform can define its event targets through the
+ * implementation of this interface.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public interface IEventTarget extends Serializable {
+
+    /**
+     * <p>
+     * Returns the name of event-driven software platform to which the target belongs.
+     * </p>
+     * 
+     * @return name of the platform
+     */
+    public String getPlatform();
+
+    /**
+     * <p>
+     * Returns a string identifier of the target. This is very convenient for visualizations of
+     * events.
+     * </p>
+     * 
+     * @return target identifier
+     */
+    public String getStringIdentifier();
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IEventType.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IEventType.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IEventType.java	(revision 922)
@@ -0,0 +1,26 @@
+
+package de.ugoe.cs.autoquest.eventcore;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * Common interface for event types. An event type can be, e.g., a mouse click, a keyboard
+ * interactions in case of GUI platforms or a HTTP request in case of a Web application.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public interface IEventType extends Serializable {
+
+    /**
+     * <p>
+     * Returns the name of the event type.
+     * </p>
+     * 
+     * @return name of the event type
+     */
+    public String getName();
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IReplayable.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IReplayable.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/IReplayable.java	(revision 922)
@@ -0,0 +1,37 @@
+package de.ugoe.cs.autoquest.eventcore;
+
+import java.io.Serializable;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+
+/**
+ * <p>
+ * This interface is used by {@link ReplayableEvent}to describe how events can
+ * be replayed. It can be used to define a sequence of fine-grained platform
+ * events that make up an abstract event.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface IReplayable extends Serializable {
+
+	/**
+	 * <p>
+	 * Returns a string to be written to the replay script that describes the
+	 * replayable platform event.
+	 * </p>
+	 * 
+	 * @return string for the replay script
+	 */
+	String getReplay();
+	
+	/**
+	 * <p>
+	 * Returns the replay decorator associated with the replayable. Returns null if no replay decorator is associated with the replayable.
+	 * </p>
+	 *
+	 * @return replay decorator
+	 */
+	IReplayDecorator getDecorator();
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/StringEventType.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/StringEventType.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/StringEventType.java	(revision 922)
@@ -0,0 +1,89 @@
+package de.ugoe.cs.autoquest.eventcore;
+
+/**
+ * <p>
+ * A simple event type that is identified by a string.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: Aug 16, 2012$
+ * @author 2012, last modified by $Author: sherbold$
+ */
+public class StringEventType implements IEventType {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * String that identifies the event type.
+     * </p>
+     */
+    private String str;
+
+    /**
+     * <p>
+     * Constructor. Creates a new StringEventType. str must not be null.
+     * </p>
+     * 
+     * @param str
+     *            string that identifies the event type
+     * @throws IllegalArgumentException
+     *             thrown if str is null
+     */
+    public StringEventType(String str) throws IllegalArgumentException {
+        if (str == null) {
+            throw new IllegalArgumentException("str must not be null");
+        }
+        this.str = str;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventType#getName()
+     */
+    @Override
+    public String getName() {
+        return "StringEventType";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return str;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return str.hashCode();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other instanceof StringEventType) {
+            return str.equals(((StringEventType) other).str);
+        }
+        return false;
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/IInteraction.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/IInteraction.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/IInteraction.java	(revision 922)
@@ -0,0 +1,34 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+/**
+ * <p>
+ * An interaction is a special event type which represents the interaction of a user with an element
+ * of a GUI. An example is a mouse click on a button.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IInteraction extends IEventType {
+    /**
+     * <p>
+     * Determines whether an event type starts a logical sequence, i.e., a task.
+     * </p>
+     * 
+     * @return true if a logical sequence is started; false otherwise
+     */
+    public boolean startsLogicalSequence();
+
+    /**
+     * <p>
+     * Determines whether an event type finishes a logical sequence, i.e., a task.
+     * </p>
+     * 
+     * @return true if a logical sequence is finished; false otherwise
+     */
+    public boolean finishesLogicalSequence();
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteraction.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteraction.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteraction.java	(revision 922)
@@ -0,0 +1,54 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+
+/**
+ * <p>
+ * Base class for all keyboard interaction event types.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public abstract class KeyInteraction implements IInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * the key associated with the interaction
+     * </p>
+     */
+    private VirtualKey key;
+
+    /**
+     * <p>
+     * Constructor. Creates a new KeyInteraction.
+     * </p>
+     * 
+     * @param key
+     *            key associated with the interaction
+     */
+    public KeyInteraction(VirtualKey key) {
+        super();
+        this.key = key;
+    }
+
+    /**
+     * <p>
+     * Returns the key associated with the interaction.
+     * </p>
+     * 
+     * @return the key
+     */
+    public VirtualKey getKey() {
+        return key;
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionCorrector.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionCorrector.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionCorrector.java	(revision 922)
@@ -0,0 +1,279 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * This class provides the functionality to sort and clean up all key interactions in a log. In
+ * particular:
+ * <ol>
+ * <li>In case a combination key (e.g., shift, alt, control) is held down, multiple
+ * {@link KeyPressed} events are logged, even though only the first one is of importance. This class
+ * removes all {@link KeyPressed} events for combination keys except the first.</li>
+ * <li>In case a normal key is held down, multiple {@link KeyPressed} events are logged, but there
+ * is only one {@link KeyReleased} event. This class adds a {@link KeyReleased} event for all such
+ * {@link KeyPressed} events.</li>
+ * <li>Due to message filtering of applications, it is possible that a {@link KeyReleased} event
+ * without a preceding {@link KeyPressed} event is logged. This class either adds the missing
+ * {@link KeyPressed} right in front of the {@link KeyReleased} or removes the {@link KeyReleased}
+ * depending on the {@link #mode}.
+ * <li>As a result of steps 2-3, we have always a matching {@link KeyPressed}/{@link KeyReleased}
+ * pairs for all normal keys. This class replaces these pairs with a {@link KeyTyped} event at the
+ * position of the {@link KeyPressed} event.</li>
+ * <li>Sometimes combination keys are not released in the same order they have been pressed. This
+ * class ensures that the {@link KeyReleased} are in the opposite order of the {@link KeyPressed}
+ * events for all combination keys.</li>
+ * </ol>
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class KeyInteractionCorrector {
+
+    /**
+     * <p>
+     * Describes the clean-up mode.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Steffen Herbold
+     */
+    public static enum CleanupMode {
+        /**
+         * <p>
+         * Single {@link KeyReleased} are removed from the sequence.
+         * </p>
+         */
+        REMOVAL,
+
+        /**
+         * <p>
+         * {@link KeyPressed} events are added to single {@link KeyReleased} events
+         * </p>
+         */
+        ADDITION
+    };
+
+    /**
+     * <p>
+     * 
+     * </p>
+     */
+    private final CleanupMode mode;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link KeyInteractionCorrector} with {@link #mode}=
+     * {@link CleanupMode#ADDITION}.
+     * </p>
+     */
+    public KeyInteractionCorrector() {
+        this(CleanupMode.ADDITION);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link KeyInteractionCorrector}.
+     * </p>
+     * 
+     * @param mode
+     *            {@link #mode} of the instance
+     */
+    public KeyInteractionCorrector(CleanupMode mode) {
+        this.mode = mode;
+    }
+
+    /**
+     * <p>
+     * Sorts and cleans up key interactions according to the class specification (@see
+     * {@link KeyInteractionCorrector} class comment).
+     * </p>
+     * <p>
+     * This method returns a sorted copy of a sequence, the sequence itself is not changed.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence which is sorted
+     * @return sorted copy of sequence
+     */
+    public List<Event> sortKeyInteractions(final List<Event> sequence) {
+        List<Event> sortedSequence = new LinkedList<Event>(sequence);
+
+        handleIncompleteKeyPairs(sortedSequence);
+        sortCombinationKeyPairs(sortedSequence);
+
+        return sortedSequence;
+    }
+
+    /**
+     * <p>
+     * Performs tasks 1-4 defined in the class description. Operations are performed in-place on the
+     * passed sequence.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence which is sorted
+     */
+    private void sortCombinationKeyPairs(List<Event> sequence) {
+        LinkedList<VirtualKey> pressedCombinationKeys = new LinkedList<VirtualKey>();
+
+        for (int i = 0; i < sequence.size(); i++) {
+            Event event = sequence.get(i);
+            if (event.getType() instanceof KeyPressed) {
+                final VirtualKey key = ((KeyPressed) event.getType()).getKey();
+                if (key.isCombinationKey()) {
+                    pressedCombinationKeys.add(key);
+                }
+            }
+            if (event.getType() instanceof KeyReleased) {
+                final VirtualKey key = ((KeyReleased) event.getType()).getKey();
+                if (key.isCombinationKey()) {
+                    /*
+                     * if( pressedCombinationKeys.isEmpty() ) { Console.traceln(Level.INFO, "" + i);
+                     * for( int j=i-30 ; j<=i ; j++ ) { Console.traceln(Level.INFO,
+                     * sequence.get(i).toString()); } }
+                     */
+                    if (key.equals(pressedCombinationKeys.getLast())) {
+                        pressedCombinationKeys.removeLast();
+                    }
+                    else {
+                        // look-ahead to find new position
+                        int offset;
+                        for (offset = 1; offset + i < sequence.size(); offset++) {
+                            Event lookaheadEvent = sequence.get(i + offset);
+                            if (lookaheadEvent.getType() instanceof KeyReleased) {
+                                if (((KeyReleased) lookaheadEvent.getType()).getKey()
+                                    .equals(pressedCombinationKeys.getLast()))
+                                {
+                                    break;
+                                }
+                            }
+                        }
+                        sequence.add(i + offset + 1, event);
+                        sequence.remove(i);
+                        i--;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Performs task 5 defined in the class description. Operations are performed in-place on the
+     * passed sequence.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence which is sorted
+     */
+    private void handleIncompleteKeyPairs(List<Event> sequence) {
+        Set<VirtualKey> pressedKeys = new HashSet<VirtualKey>();
+        int firstPressedIndex = -1;
+
+        Set<VirtualKey> pressedCombinationKeysSession = new HashSet<VirtualKey>();
+
+        for (int i = 0; i < sequence.size(); i++) {
+            Event event = sequence.get(i);
+            if (event.getType() instanceof KeyboardFocusChange) {
+                if (firstPressedIndex != -1) {
+                    sequence.remove(i);
+                    sequence.add(firstPressedIndex, event);
+                }
+            }
+
+            if (event.getType() instanceof KeyPressed) {
+                if (pressedKeys.isEmpty()) {
+                    firstPressedIndex = i;
+                }
+                VirtualKey key = ((KeyPressed) event.getType()).getKey();
+                if (!key.isCombinationKey()) {
+                    ListIterator<Event> iter = sequence.listIterator(i);
+                    iter.next();
+                    iter.set(new Event(new KeyTyped(key), event.getTarget()));
+                }
+                else {
+                    pressedCombinationKeysSession.add(key);
+                    if (pressedKeys.contains(key)) {
+                        sequence.remove(i);
+                        i--;
+                    }
+                }
+                pressedKeys.add(key);
+            }
+            if (event.getType() instanceof KeyReleased) {
+                VirtualKey key = ((KeyReleased) event.getType()).getKey();
+                if (!key.isCombinationKey()) {
+                    if (pressedKeys.contains(key)) {
+                        sequence.remove(i);
+                        i--;
+                    }
+                    else {
+                        // found KeyReleased event without KeyPressed
+                        switch (mode)
+                        {
+                            case REMOVAL:
+                                sequence.remove(i);
+                                i--;
+                                break;
+                            case ADDITION:
+                                ListIterator<Event> iter = sequence.listIterator(i);
+                                iter.next();
+                                iter.set(new Event(new KeyTyped(key), event.getTarget()));
+                                break;
+                            default:
+                                throw new AssertionError(
+                                                         "reached source code that should be unreachable");
+                        }
+                    }
+                }
+                else {
+                    if (!pressedKeys.contains(key)) {
+                        if (pressedCombinationKeysSession.contains(key)) {
+                            Console.traceln(Level.SEVERE, "Found a " + key +
+                                " KeyReleased event without a KeyPressed event." +
+                                "The event will be dropped and the session is possibly faulty.");
+                            sequence.remove(i);
+                            i--;
+                        }
+                        else {
+                            Console
+                                .traceln(Level.SEVERE,
+                                         "Found a " +
+                                             key +
+                                             " KeyReleased event without a KeyPressed event." +
+                                             "Since no KeyPressed of key " +
+                                             key +
+                                             " has been part of the session " +
+                                             "till now, we assume that the key has been pressed since the beginning " +
+                                             "of the session and add a KeyPressed event for " +
+                                             key + " to the start " + "of the session.");
+                            sequence.add(0, new Event(new KeyPressed(key), event.getTarget()));
+                            i++;
+                        }
+                    }
+                }
+                pressedKeys.remove(key);
+                if (pressedKeys.isEmpty()) {
+                    firstPressedIndex = -1;
+                }
+            }
+        }
+        if (!pressedKeys.isEmpty()) {
+            Console
+                .traceln(Level.WARNING,
+                         "There was probably a failure during the handling of incomplete key event pairs.");
+        }
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionTargetCorrector.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionTargetCorrector.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyInteractionTargetCorrector.java	(revision 922)
@@ -0,0 +1,68 @@
+// Module    : $RCSfile: KeyInteractionTargetCorrector.java,v $
+// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 29.08.2012 $
+// Project   : quest-core-events
+// Creation  : 2012 by pharms
+// Copyright : Patrick Harms, 2012
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+
+/**
+ * <p>
+ * This class iterates the provided sequence and sets the target of all key interaction events
+ * to the GUI element having the current keyboard focus. The current keyboard focus is determined
+ * either by keyboard focus events or by using the target of the first key interaction in the
+ * provided sequence. Events changing the keyboard focus are discarded herewith.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 29.08.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class KeyInteractionTargetCorrector {
+    
+    /**
+     * <p>
+     * This method performs the work described in the description of the class. A new list is
+     * instantiated and returned. This list is filled with the events provided by the sequence
+     * being the parameter of the method except for key interaction events. Those are replaced
+     * by a new event with the identical event type but the corrected event target.
+     * </p>
+     * 
+     * @param sequence the event sequence to correct the key interactions targets in
+     * 
+     * @return the resulting sequence, in which key interactions have the correct target, i.e.
+     *         the GUI element having the keyboard focus
+     */
+    public List<Event> correctKeyInteractionTargets(List<Event> sequence) {
+        List<Event> resultingSequence = new LinkedList<Event>();
+        IGUIElement currentKeyboardFocusGUIElement = null;
+        
+        for (Event event : sequence) {
+            if (event.getTarget() instanceof IGUIElement) {
+                if (event.getType() instanceof KeyboardFocusChange) {
+                    currentKeyboardFocusGUIElement = (IGUIElement) event.getTarget();
+                    event = null;
+                }
+                else if (event.getType() instanceof KeyInteraction) {
+                    if (currentKeyboardFocusGUIElement == null) {
+                        currentKeyboardFocusGUIElement = (IGUIElement) event.getTarget();
+                    }
+                    
+                    if (!currentKeyboardFocusGUIElement.equals(event.getTarget())) {
+                        event = new Event(event.getType(), currentKeyboardFocusGUIElement);
+                    }
+                }
+            }
+            
+            if (event != null) {
+                resultingSequence.add(event);
+            }
+        }
+        
+        return resultingSequence;
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyPressed.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyPressed.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyPressed.java	(revision 922)
@@ -0,0 +1,97 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+
+/**
+ * <p>
+ * Event type for pressing down a key.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class KeyPressed extends KeyInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link KeyPressed} event type.
+     * </p>
+     * 
+     * @see KeyInteraction#KeyInteraction(VirtualKey)
+     */
+    public KeyPressed(VirtualKey key) {
+        super(key);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return "KeyPressed " + super.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "pressing key " + super.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        // TODO handle lock keys correctly
+        return super.getKey().isCombinationKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof KeyPressed) {
+            return (super.getKey() == ((KeyPressed) other).getKey());
+        }
+        else {
+            return false;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return super.getKey().hashCode();
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyReleased.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyReleased.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyReleased.java	(revision 922)
@@ -0,0 +1,94 @@
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+
+/**
+ * <p>
+ * Event type for releasing a key.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class KeyReleased extends KeyInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link KeyReleased} event type.
+     * </p>
+     * 
+     * @see KeyInteraction#KeyInteraction(VirtualKey)
+     */
+    public KeyReleased(VirtualKey key) {
+        super(key);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return "KeyReleased " + super.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "releasing key " + super.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        // TODO handle lock keys correctly
+        return super.getKey().isCombinationKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof KeyReleased) {
+            return (super.getKey() == ((KeyReleased) other).getKey());
+        }
+        else {
+            return false;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return super.getKey().hashCode();
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyTyped.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyTyped.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyTyped.java	(revision 922)
@@ -0,0 +1,98 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+
+/**
+ * <p>
+ * Event type for typing a key, i.e., pressing and releasing it right away.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class KeyTyped extends KeyInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link KeyTyped} event type.
+     * </p>
+     * 
+     * @see KeyInteraction#KeyInteraction(VirtualKey)
+     */
+    public KeyTyped(VirtualKey key) {
+        super(key);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return "KeyTyped " + super.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "typed key " + super.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        // TODO handle lock keys correctly
+        return super.getKey().isCombinationKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        // TODO handle lock keys correctly
+        return super.getKey().isCombinationKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof KeyTyped) {
+            return (super.getKey() == ((KeyTyped) other).getKey());
+        }
+        else {
+            return false;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return super.getKey().hashCode();
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyboardFocusChange.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyboardFocusChange.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/KeyboardFocusChange.java	(revision 922)
@@ -0,0 +1,83 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Event type for keyboard focus changes. The target of the respective event has obtained the
+ * keyboard focus of the application.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class KeyboardFocusChange implements IInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return "KeyboardFocusChange";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.userinteraction.Interaction#startsLogicalSequence()
+     */
+    @Override
+    public boolean startsLogicalSequence() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    @Override
+    public boolean finishesLogicalSequence() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "keyboard focus changed";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof KeyboardFocusChange) {
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getClass().hashCode();
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonDown.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonDown.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonDown.java	(revision 922)
@@ -0,0 +1,114 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Event type for pressing a mouse button.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MouseButtonDown extends MouseButtonInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link MouseButtonDown} event type.
+     * </p>
+     * 
+     * @see MouseButtonInteraction#MouseButtonInteraction(Button)
+     */
+    public MouseButtonDown(Button button) {
+        super(button);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        if (super.getButton() == Button.LEFT) {
+            return "LeftMouseButtonDown";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "MiddleMouseButtonDown";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "RightMouseButtonDown";
+        }
+        else {
+            return "UnknownMouseButtonDown";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (super.getButton() == Button.LEFT) {
+            return "left mouse button down";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "middle mouse button down";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "right mouse button down";
+        }
+        else {
+            return "unknown mouse button down";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MouseButtonDown) {
+            return getButton().equals(((MouseButtonDown) obj).getButton());
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getButton().hashCode();
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonInteraction.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonInteraction.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonInteraction.java	(revision 922)
@@ -0,0 +1,85 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Base class for all mouse interaction event types.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public abstract class MouseButtonInteraction extends MouseInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Describes the pressed mouse button.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Patrick Harms
+     */
+    public static enum Button {
+        LEFT, MIDDLE, RIGHT, X;
+    }
+
+    /**
+     * <p>
+     * The button used for mouse interaction
+     * </p>
+     */
+    private Button button;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link MouseButtonInteraction}
+     * </p>
+     * 
+     * @param button
+     *            the button associated with the interaction
+     */
+    public MouseButtonInteraction(Button button) {
+        this.button = button;
+    }
+
+    /**
+     * <p>
+     * Returns the button associated with the interaction.
+     * </p>
+     * 
+     * @return the button
+     */
+    public Button getButton() {
+        return button;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MouseButtonInteraction) {
+            return getButton().equals(((MouseButtonInteraction) obj).getButton());
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getButton().hashCode();
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonUp.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonUp.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseButtonUp.java	(revision 922)
@@ -0,0 +1,113 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Event type for releasing a mouse button.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MouseButtonUp extends MouseButtonInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link MouseButtonUp} event type.
+     * </p>
+     * 
+     * @see MouseButtonInteraction#MouseButtonInteraction(Button)
+     */
+    public MouseButtonUp(Button button) {
+        super(button);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        if (super.getButton() == Button.LEFT) {
+            return "LeftMouseButtonUp";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "MiddleMouseButtonUp";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "RightMouseButtonUp";
+        }
+        else {
+            return "UnknownMouseButtonUp";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (super.getButton() == Button.LEFT) {
+            return "left mouse button up";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "middle mouse button up";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "right mouse button up";
+        }
+        else {
+            return "unknown mouse button up";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MouseButtonUp) {
+            return getButton().equals(((MouseButtonUp) obj).getButton());
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getButton().hashCode();
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClick.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClick.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClick.java	(revision 922)
@@ -0,0 +1,113 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Event type for a mouse click, i.e., pressing and releasing it right away.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MouseClick extends MouseButtonInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link MouseClick} event type.
+     * </p>
+     * 
+     * @see MouseButtonInteraction#MouseButtonInteraction(Button)
+     */
+    public MouseClick(Button button) {
+        super(button);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        if (super.getButton() == Button.LEFT) {
+            return "LeftMouseClick";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "MiddleMouseClick";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "RightMouseClick";
+        }
+        else {
+            return "UnknownMouseButtonClick";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (super.getButton() == Button.LEFT) {
+            return "left mouse click";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "middle mouse click";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "right mouse click";
+        }
+        else {
+            return "unknown mouse button click";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MouseClick) {
+            return getButton().equals(((MouseClick) obj).getButton());
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getButton().hashCode();
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClickCondenser.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClickCondenser.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseClickCondenser.java	(revision 922)
@@ -0,0 +1,107 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+
+/**
+ * <p>
+ * This class condenses mouse clicks, i.e. it reduces a sequence of mouse button down, mouse button
+ * up and mouse click with the same button on the same event target to a single mouse click with
+ * that button on that target. The mouse button down and mouse button up events are discarded. For
+ * this, it iterates the provided sequence and identifies any match of the named event sequence
+ * pattern. This match is condensed to the mouse click event.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MouseClickCondenser {
+
+    /**
+     * <p>
+     * This method performs the work described in the description of the class. A new list is
+     * instantiated and returned. This list is filled with the events provided by the sequence being
+     * the parameter of the method except for mouse button down and mouse button up events which are
+     * followed by a mouse click event with the same button on the same target.
+     * </p>
+     * 
+     * @param sequence
+     *            the event sequence to condense the mouse clicks in
+     * 
+     * @return the resulting sequence, in which mouse clicks are condensed to single mouse click
+     *         events
+     */
+    public List<Event> condenseMouseClicks(List<Event> sequence) {
+        List<Event> resultingSequence = new LinkedList<Event>();
+
+        int index = 0;
+        while (index < sequence.size()) // -2 because we don't need to go to the end
+        {
+            if ((index + 2) < sequence.size()) {
+                Event mouseButtonDown = sequence.get(index);
+                Event mouseButtonUp = sequence.get(index + 1);
+                Event mouseClick = sequence.get(index + 2);
+                if (mouseClickSequenceFound(mouseButtonDown, mouseButtonUp, mouseClick)) {
+                    // skip the mouse button down and mouse button up event
+                    index += 2;
+                }
+            }
+
+            resultingSequence.add(sequence.get(index));
+            index++;
+        }
+
+        return resultingSequence;
+    }
+
+    /**
+     * 
+     */
+    private boolean mouseClickSequenceFound(Event mouseButtonDown,
+                                            Event mouseButtonUp,
+                                            Event mouseClick)
+    {
+        // check the first in a row of three for validity
+        if (!(mouseButtonDown.getType() instanceof MouseButtonDown)) {
+            return false;
+        }
+
+        // check the second node for validity
+        if (!(mouseButtonUp.getType() instanceof MouseButtonUp)) {
+            return false;
+        }
+
+        IEventTarget eventTarget = mouseButtonDown.getTarget();
+
+        if (!eventTarget.equals(mouseButtonUp.getTarget())) {
+            return false;
+        }
+
+        MouseButtonInteraction.Button button =
+            ((MouseButtonDown) mouseButtonDown.getType()).getButton();
+
+        if (!button.equals(((MouseButtonUp) mouseButtonUp.getType()).getButton())) {
+            return false;
+        }
+
+        // check the third node for validity
+        if (!(mouseClick.getType() instanceof MouseClick)) {
+            return false;
+        }
+
+        if (!eventTarget.equals(mouseClick.getTarget())) {
+            return false;
+        }
+
+        if (!button.equals(((MouseClick) mouseClick.getType()).getButton())) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseDoubleClick.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseDoubleClick.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseDoubleClick.java	(revision 922)
@@ -0,0 +1,114 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Event type for a double click, i.e., pressing the mouse, releasing it, pressing it, and releasing
+ * it again right away.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class MouseDoubleClick extends MouseButtonInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link MouseDoubleClick} event type.
+     * </p>
+     * 
+     * @see MouseButtonInteraction#MouseButtonInteraction(Button)
+     */
+    public MouseDoubleClick(Button button) {
+        super(button);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        if (super.getButton() == Button.LEFT) {
+            return "LeftMouseDoubleClick";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "MiddleMouseDoubleClick";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "RightMouseDoubleClick";
+        }
+        else {
+            return "UnknownMouseButtonDoubleClick";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (super.getButton() == Button.LEFT) {
+            return "left mouse double click";
+        }
+        else if (super.getButton() == Button.MIDDLE) {
+            return "middle mouse double click";
+        }
+        else if (super.getButton() == Button.RIGHT) {
+            return "right mouse double click";
+        }
+        else {
+            return "unknown mouse button double click";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MouseDoubleClick) {
+            return getButton().equals(((MouseDoubleClick) obj).getButton());
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getButton().hashCode();
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseInteraction.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseInteraction.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/MouseInteraction.java	(revision 922)
@@ -0,0 +1,20 @@
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Base class for all mouse interaction event types.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public abstract class MouseInteraction implements IInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextInput.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextInput.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextInput.java	(revision 922)
@@ -0,0 +1,211 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import java.util.Collections;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * A text input represents a list of key events that together represent entering text into a text
+ * field or text area.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms, Steffen Herbold
+ */
+public class TextInput implements IInteraction {
+
+    /**
+     * <p>
+     * Defines how the {@link TextInput}s are evaluated as equal.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Steffen Herbold
+     */
+    public enum TextEquality {
+        /**
+         * <p>
+         * Two text inputs are equal if their {@link TextInput#textInputEvents} are equal.
+         * </p>
+         */
+        LEXICAL,
+        /**
+         * <p>
+         * Two text inputs are equal if their {@link TextInput#enteredText}s are equal.
+         * </p>
+         */
+        SYNTACTICAL,
+        /**
+         * <p>
+         * All text inputs are equal.
+         * </p>
+         */
+        SEMANTICAL
+    };
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * The text resulting from the text input events.
+     * </p>
+     */
+    private String enteredText;
+
+    /**
+     * <p>
+     * The text input events that caused the entering of the text.
+     * </p>
+     */
+    private List<Event> textInputEvents;
+
+    /**
+     * <p>
+     * Defines how this TextInput event evaluates the equality.
+     * </p>
+     */
+    private final TextEquality equalityType;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link TextInput} with {@link TextEquality#LEXICAL} equality.
+     * </p>
+     * 
+     * @param enteredText
+     *            text resulting from the inputs
+     * @param textInputEvents
+     *            text input events of which this input consists
+     */
+    public TextInput(String enteredText, List<Event> textInputEvents) {
+        this(enteredText, textInputEvents, TextEquality.LEXICAL);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link TextInput}..
+     * </p>
+     * 
+     * @param enteredText
+     *            text resulting from the inputs
+     * @param textInputEvents
+     *            text input events of which this input consists
+     * @param equalityType
+     *            defines how this event evaluates the equality (@see {@link TextEquality})
+     */
+    public TextInput(String enteredText, List<Event> textInputEvents, TextEquality equalityType) {
+        this.enteredText = enteredText;
+        this.textInputEvents = textInputEvents;
+        this.equalityType = equalityType;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return "TextInput(\"" + enteredText + "\")";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "text input \"" + enteredText + "\"";
+    }
+
+    /**
+     * <p>
+     * Returns the entered text.
+     * </p>
+     * 
+     * @return the entered text
+     */
+    public String getEnteredText() {
+        return enteredText;
+    }
+
+    /**
+     * <p>
+     * Returns the events of which this {@link TextInput} consists. The returned list is immutable.
+     * </p>
+     * 
+     * @return the textInputEvents
+     */
+    public List<Event> getTextInputEvents() {
+        return Collections.unmodifiableList(textInputEvents);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        else if (obj instanceof TextInput) {
+            switch (equalityType)
+            {
+                case LEXICAL:
+                    return textInputEvents.equals(((TextInput) obj).textInputEvents);
+                case SYNTACTICAL:
+                    return enteredText.equals(((TextInput) obj).enteredText);
+                case SEMANTICAL:
+                    return true;
+                default:
+                    throw new AssertionError("reached source code that should be unreachable");
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hashCode = getClass().hashCode();
+        if (equalityType == TextEquality.LEXICAL) {
+            hashCode += enteredText.hashCode() + textInputEvents.size();
+        }
+        else if (equalityType == TextEquality.SYNTACTICAL) {
+            hashCode += enteredText.hashCode();
+        }
+        return hashCode;
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextInputDetector.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextInputDetector.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextInputDetector.java	(revision 922)
@@ -0,0 +1,330 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput.TextEquality;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
+import de.ugoe.cs.autoquest.keyboardmaps.KeyboardMap;
+import de.ugoe.cs.autoquest.keyboardmaps.KeyboardMapFactory;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+
+/**
+ * <p>
+ * The text input detector iterates a list of events and searches for subsequent key events. Those
+ * are replaced by a single text input event representing the text entered through the key events.
+ * The replacement is only done, if the key events have a text field or text area as target
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 18.03.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TextInputDetector {
+
+    /** the keyboard map to use for character recognition */
+    private KeyboardMap keyboardMap = KeyboardMapFactory.createKeyboardMap(Locale.GERMAN);
+
+    /** the keys pressed in parallel */
+    List<VirtualKey> pressedKeys = new ArrayList<VirtualKey>();
+
+    private final TextEquality textEqualityType;
+
+    /**
+     * <p>
+     * Constructor. Creates a new TextInputDectector that generates {@link TextInput} with
+     * {@link TextEquality#LEXICAL} equality.
+     * </p>
+     * 
+     */
+    public TextInputDetector() {
+        this(TextEquality.LEXICAL);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new TextInputDectector that generates {@link TextInput} with a given
+     * {@link TextEquality} type.
+     * </p>
+     * 
+     * @param textEqualityType
+     *            equality type of the generated events
+     */
+    public TextInputDetector(TextEquality textEqualityType) {
+        this.textEqualityType = textEqualityType;
+    }
+
+    /**
+     * <p>
+     * in the provided list of events, this method detects any event sequences that consists of key
+     * interactions and replaces them with a single text input interaction. This contains the
+     * entered text as well as the replaced key interaction events
+     * </p>
+     * 
+     * @param sequence
+     *            the event sequence to search for text input events
+     * 
+     * @return the resulting sequence, in which key interactions on text fields and areas are
+     *         reduced to text input interactions
+     */
+    public List<Event> detectTextInputs(List<Event> sequence) {
+        List<Event> resultingSequence = new LinkedList<Event>();
+
+        int textEntryStartIndex = -1;
+        IEventTarget lastEventTarget = null;
+
+        int index = 0;
+        Event currentEvent = null;
+        Event textInputEvent = null;
+        while (index < sequence.size()) {
+            currentEvent = sequence.get(index);
+            textInputEvent = null;
+
+            if (isKeyInteraction(currentEvent) && isDataInputEventTarget(currentEvent.getTarget()))
+            {
+                if (textEntryStartIndex < 0) {
+                    textEntryStartIndex = index;
+                    lastEventTarget = currentEvent.getTarget();
+                }
+                else if (!lastEventTarget.equals(currentEvent.getTarget())) {
+                    textInputEvent =
+                        handleTextEntrySequence(sequence, textEntryStartIndex, index - 1,
+                                                lastEventTarget);
+
+                    textEntryStartIndex = index;
+                    lastEventTarget = currentEvent.getTarget();
+                }
+                currentEvent = null;
+            }
+            else {
+                if (textEntryStartIndex >= 0) {
+                    textInputEvent =
+                        handleTextEntrySequence(sequence, textEntryStartIndex, index - 1,
+                                                lastEventTarget);
+
+                    textEntryStartIndex = -1;
+                    lastEventTarget = null;
+                }
+
+            }
+
+            if (textInputEvent != null) {
+                resultingSequence.add(textInputEvent);
+            }
+
+            if (currentEvent != null) {
+                resultingSequence.add(currentEvent);
+            }
+
+            index++;
+        }
+
+        if (textEntryStartIndex >= 0) {
+            textInputEvent =
+                handleTextEntrySequence(sequence, textEntryStartIndex, sequence.size() - 1,
+                                        lastEventTarget);
+
+            if (textInputEvent != null) {
+                resultingSequence.add(textInputEvent);
+            }
+        }
+
+        return resultingSequence;
+    }
+
+    /**
+     * <p>
+     * returns true if the provide event is a key interaction; false else
+     * </p>
+     * 
+     * @param event
+     *            the even to check
+     * 
+     * @return as described
+     */
+    private boolean isKeyInteraction(Event event) {
+        return (event.getType() instanceof KeyInteraction);
+    }
+
+    /**
+     * <p>
+     * creates a single text input event as replacement for key interactions being part of the
+     * subsequence of events denoted by the start and end index in the provide event sequence. If no
+     * text was entered, because the subsequence only contained key released events, then no text
+     * input event is generated (the method returns null).
+     * </p>
+     * 
+     * @param sequence
+     *            the event sequence of which the subsequence is analyzed
+     * @param startIndex
+     *            the start index in the event sequence from which the analysis should start
+     *            (inclusive)
+     * @param endIndex
+     *            the end index in the event sequence where the analysis should end (inclusive)
+     * @param eventTarget
+     *            the event target to be used for the new event
+     * 
+     * @return a text input event representing the text input resulting from the events of the
+     *         provided subsequence
+     * 
+     * @throws IllegalArgumentException
+     *             if the denoted subsequence contains other events than key interactions
+     */
+    private Event handleTextEntrySequence(List<Event> sequence,
+                                          int startIndex,
+                                          int endIndex,
+                                          IEventTarget eventTarget)
+    {
+        List<Event> textInputEvents = new ArrayList<Event>();
+
+        String enteredText = determineEnteredText(sequence, startIndex, endIndex, textInputEvents);
+
+        if ((enteredText != null) && (!"".equals(enteredText))) {
+            TextInput textInput = new TextInput(enteredText, textInputEvents, textEqualityType);
+            return new Event(textInput, eventTarget);
+        }
+        else {
+            return null;
+        }
+    }
+
+    /**
+     * <p>
+     * check if an event target is a data input field, i.e. a text field or a text area
+     * </p>
+     * 
+     * @param eventTarget
+     *            the event target to check
+     * 
+     * @return true, if it is a text field or a text area ; false else
+     */
+    private boolean isDataInputEventTarget(IEventTarget eventTarget) {
+        return ((eventTarget instanceof ITextField) || (eventTarget instanceof ITextArea));
+    }
+
+    /**
+     * <p>
+     * determines the text entered in the event subsequence denoted by the start and end index of
+     * the provided event sequence. The method records any pressed and released key interaction as
+     * well as combinations of pressed key interactions (such as shift + letter) and determines the
+     * respective character. All identified characters are then combined to the entered text. The
+     * method also identifies the usage of the back space. The enter key is ignored for text fields
+     * in which pressing the enter key results in finishing the text entry. All analyzed key
+     * interaction events are stored in the provided text input events result list. If the
+     * subsequence only contains key released events, no text was entered and the returned text is
+     * null.
+     * </p>
+     * 
+     * @param sequence
+     *            the event sequence of which the subsequence is analyzed
+     * @param startIndex
+     *            the start index in the event sequence from which the analysis should start
+     *            (inclusive)
+     * @param endIndex
+     *            the end index in the event sequence where the analysis should end (inclusive)
+     * @param textInputEvents
+     *            a buffer to contain any key interaction event analyzed (in out)
+     * 
+     * @return the text entered through the interaction events of the denoted subsequence
+     * 
+     * @throws IllegalArgumentException
+     *             if the denoted sequence contains other events than key interactions
+     */
+    private String determineEnteredText(List<Event> sequence,
+                                        int startIndex,
+                                        int endIndex,
+                                        List<Event> textInputEvents)
+        throws IllegalArgumentException
+    {
+        Event event;
+        StringBuffer enteredText = new StringBuffer();
+
+        for (int i = startIndex; i <= endIndex; i++) {
+            event = sequence.get(i);
+
+            if (event.getType() instanceof KeyPressed || event.getType() instanceof KeyTyped) {
+                VirtualKey key = ((KeyInteraction) event.getType()).getKey();
+
+                pressedKeys.add(key);
+
+                if (key == VirtualKey.BACK_SPACE) {
+                    if (enteredText.length() > 0) {
+                        enteredText.deleteCharAt(enteredText.length() - 1);
+                    }
+                }
+                else if (key == VirtualKey.ENTER) {
+                    // text fields only contain one line of code. Therefore the return is ignored.
+                    if (!(event.getTarget() instanceof ITextField)) {
+                        enteredText.append(getCharacter(key, pressedKeys));
+                    }
+                }
+                else {
+                    char theChar = getCharacter(key, pressedKeys);
+                    if (theChar != Character.UNASSIGNED) {
+                        enteredText.append(theChar);
+                    }
+                }
+            }
+            else if (event.getType() instanceof KeyReleased || event.getType() instanceof KeyTyped)
+            {
+                pressedKeys.remove(((KeyInteraction) event.getType()).getKey());
+            }
+            else {
+                throw new IllegalArgumentException(
+                                                   "the subsequence denoted by the indexes contains other interactions than "
+                                                       + "just key strokes");
+            }
+
+            textInputEvents.add(event);
+        }
+
+        if (enteredText.length() > 0) {
+            return enteredText.toString();
+        }
+        else {
+            return null;
+        }
+    }
+
+    /**
+     * <p>
+     * determines the character matching the pressed key depending on other keys pressed in parallel
+     * such as the shift key.
+     * </p>
+     * 
+     * @param key
+     *            the key for which the character shall be determined
+     * @param pressedKeys
+     *            the list of other keys pressed in parallel
+     * 
+     * @return the character resulting from the combination of pressed keys
+     */
+    private char getCharacter(VirtualKey key, List<VirtualKey> pressedKeys) {
+        boolean numlock = false;
+        boolean shift = false;
+        boolean altgr = false;
+
+        for (VirtualKey pressedKey : pressedKeys) {
+            if (pressedKey.isShiftKey()) {
+                shift = !shift;
+            }
+            else if (pressedKey == VirtualKey.ALT_GRAPH) {
+                altgr = !altgr;
+            }
+            else if (pressedKey == VirtualKey.NUM_LOCK) {
+                numlock = !numlock;
+            }
+        }
+
+        return keyboardMap.getCharacterFor(key, numlock, shift, altgr, false);
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextSelection.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextSelection.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/TextSelection.java	(revision 922)
@@ -0,0 +1,81 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Event type for selecting text.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class TextSelection implements IInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return "TextSelection";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "text selection";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof TextSelection) {
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getClass().hashCode();
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/ValueSelection.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/ValueSelection.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/gui/ValueSelection.java	(revision 922)
@@ -0,0 +1,115 @@
+
+package de.ugoe.cs.autoquest.eventcore.gui;
+
+/**
+ * <p>
+ * Event type for selecting a value.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class ValueSelection<T> implements IInteraction {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * The selected value.
+     * </p>
+     */
+    private T selectedValue;
+
+    /**
+     * <p>
+     * Constructor. Creates a new ValueSelection.
+     * </p>
+     * 
+     * @param selectedValue
+     *            the selected value
+     */
+    public ValueSelection(T selectedValue) {
+        this.selectedValue = selectedValue;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return "ValueSelection";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "select value";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /**
+     * <p>
+     * Returns the selected value associated with this event.
+     * </p>
+     * 
+     * @return the selectedValue
+     */
+    public T getSelectedValue() {
+        return selectedValue;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if ((obj == null) || (!(obj instanceof ValueSelection<?>))) {
+            return false;
+        }
+
+        ValueSelection<?> otherValueSelection = (ValueSelection<?>) obj;
+
+        return ((otherValueSelection != null) && ((selectedValue == otherValueSelection.selectedValue) || ((selectedValue != null) && (selectedValue
+            .equals(otherValueSelection.selectedValue)))));
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return selectedValue.hashCode();
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/AbstractDefaultGUIElement.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/AbstractDefaultGUIElement.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/AbstractDefaultGUIElement.java	(revision 922)
@@ -0,0 +1,268 @@
+
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <p>
+ * Skeletal implementation for GUI elements.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public abstract class AbstractDefaultGUIElement implements IGUIElement {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    public static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * The reference to equal GUI element manager (needed to preserve singleton behavior, even
+     * though the objects are not singleton).
+     * </p>
+     */
+    private static final EqualGUIElementManager equalGUIElementManager =
+        new EqualGUIElementManager();
+
+    /**
+     * <p>
+     * Specification of the GUI element
+     * </p>
+     */
+    private final IGUIElementSpec specification;
+
+    /**
+     * <p>
+     * Reference to the parent element
+     * </p>
+     */
+    private final IGUIElement parent;
+
+    /**
+     * <p>
+     * Constructor. Creates a new AbstractDefaultGUIElement.
+     * </p>
+     * 
+     * @param specification
+     *            specification of the created GUI element
+     * @param parent
+     *            parent of the created GUI element; null means the element is a top-level window
+     */
+    public AbstractDefaultGUIElement(IGUIElementSpec specification, IGUIElement parent) {
+        this.specification = specification;
+        this.parent = parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.guimodel.GUIElement#getSpecification()
+     */
+    @Override
+    public IGUIElementSpec getSpecification() {
+        return specification;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#getParent()
+     */
+    @Override
+    public IGUIElement getParent() {
+        return parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#addEqualGUIElement(IGUIElement)
+     */
+    @Override
+    public void addEqualGUIElement(IGUIElement equalElement) {
+        equalGUIElementManager.addEqualGUIElements(this, equalElement);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see GUIElement#equals(GUIElement)
+     */
+    public final boolean equals(Object other) {
+        // implement final, as GUI elements are all singletons and they equal only if they are the
+        // same object or if they are in the list of equal GUI elements
+        return super.equals(other) || equalGUIElementManager.equals(this, other);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public final int hashCode() {
+        // implement final, as GUI elements are all singletons and they equal only if they are the
+        // same object. If there are several GUI element objects that represent the same GUI element
+        // then they are stored in the list of equal elements. In this case, the hash code of the
+        // list is unique within the system
+        return equalGUIElementManager.hashCode(this);
+    }
+
+    /**
+     * <p>
+     * This internal helper class manages equal GUI elements. This is necessary, as we often first
+     * identify many GUI elements as different and later use a heuristic to determine that they are
+     * the same. This class provides the means to preserve the singleton behavior of the GUI
+     * elements after we merge two GUI elements.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Patrick Harms
+     */
+    private static class EqualGUIElementManager {
+
+        /**
+         * <p>
+         * The internal map of GUI elements mapping each registered element to its equal variants.
+         * We use the {@link IdentityHashMap} as a normal hash map would not work because of the
+         * changing of the hash code of GUI elements at runtime.
+         * </p>
+         */
+        private IdentityHashMap<IGUIElement, List<IGUIElement>> identityHashMap =
+            new IdentityHashMap<IGUIElement, List<IGUIElement>>();
+
+        /**
+         * <p>
+         * Adds a new equals relationship between two {@link IGUIElement}s to the equality manager.
+         * </p>
+         * 
+         * @param guiElement1
+         *            first equal GUI element
+         * @param guiElement2
+         *            second equal GUI element
+         */
+        private synchronized void addEqualGUIElements(IGUIElement guiElement1,
+                                                      IGUIElement guiElement2)
+        {
+            List<IGUIElement> list1 = identityHashMap.get(guiElement1);
+            List<IGUIElement> list2 = identityHashMap.get(guiElement2);
+
+            if (list1 == null) {
+                if (list2 == null) {
+                    list2 = new LinkedList<IGUIElement>();
+                    list2.add(guiElement1);
+                    list2.add(guiElement2);
+                    identityHashMap.put(guiElement1, list2);
+                    identityHashMap.put(guiElement2, list2);
+                }
+                else {
+                    addIfNotContained(list2, guiElement1);
+                    identityHashMap.put(guiElement1, list2);
+                }
+            }
+            else {
+                if (list2 == null) {
+                    addIfNotContained(list1, guiElement2);
+                    identityHashMap.put(guiElement2, list1);
+                }
+                else if (list1 != list2) {
+                    list1.addAll(list2);
+                    identityHashMap.put(guiElement2, list1);
+                }
+                // else
+                // in this case, both GUI elements should already be registered with the same
+                // lists.
+            }
+        }
+
+        /**
+         * <p>
+         * Returns the object hash of a {@link IGUIElement}.
+         * </p>
+         * 
+         * @param guiElement
+         *            gui element whose object hash is determined
+         * @return the object hash
+         */
+        private synchronized int hashCode(IGUIElement guiElement) {
+            return System.identityHashCode(getEqualElementsList(guiElement));
+        }
+
+        /**
+         * <p>
+         * Determines the equality of two {@link IGUIElement}s based on the information of the
+         * identify manager. Two elements are equal, if they have been added as equal using
+         * {@link #addEqualGUIElements(IGUIElement, IGUIElement)}.
+         * </p>
+         * 
+         * @param guiElement
+         *            GUI element to which the object is compared
+         * @param other
+         *            object that is compared to the GUI element
+         * @return
+         */
+        private synchronized boolean equals(IGUIElement guiElement, Object other) {
+            if (other instanceof IGUIElement) {
+                for (IGUIElement candidate : getEqualElementsList(guiElement)) {
+                    if (candidate == other) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         * <p>
+         * Returns the equal {@link IGUIElement} of a given {@link IGUIElement}.
+         * </p>
+         * 
+         * @param guiElement
+         *            GUI element of which the equal elements are returned
+         * @return the equal GUI elements
+         */
+        private List<IGUIElement> getEqualElementsList(IGUIElement guiElement) {
+            List<IGUIElement> returnValue = identityHashMap.get(guiElement);
+
+            if (returnValue == null) {
+                returnValue = new LinkedList<IGUIElement>();
+                returnValue.add(guiElement);
+                identityHashMap.put(guiElement, returnValue);
+            }
+
+            return returnValue;
+        }
+
+        /**
+         * <p>
+         * Adds {@link IGUIElement} as equal to a list of {@link IGUIElement} if and only if it is
+         * not already contained.
+         * </p>
+         * 
+         * @param equalElementsList
+         *            list of {@link IGUIElement} to which the GUI element is added
+         * @param guiElement
+         *            GUI element to be added
+         */
+        private void addIfNotContained(List<IGUIElement> equalElementsList, IGUIElement guiElement)
+        {
+            for (IGUIElement candidate : equalElementsList) {
+                if (candidate == guiElement) {
+                    return;
+                }
+            }
+
+            equalElementsList.add(guiElement);
+        }
+
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIElementFactory.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIElementFactory.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIElementFactory.java	(revision 922)
@@ -0,0 +1,262 @@
+
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Creates {@link IGUIElement}s from a given specification. Implemented as singleton.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class GUIElementFactory implements IGUIElementFactory {
+
+    /**
+     * <p>
+     * Instance of the class (singleton)
+     * </p>
+     */
+    private static GUIElementFactory instance = new GUIElementFactory();
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIElementFactory. Private to preserve singleton property.
+     * </p>
+     */
+    private GUIElementFactory() {}
+
+    /**
+     * <p>
+     * Returns the instance of this class.
+     * </p>
+     * 
+     * @return the instance
+     */
+    public static synchronized GUIElementFactory getInstance() {
+        return instance;
+    }
+
+    /**
+     * <p>
+     * A property mapping that defines to which Java class is created given the type of the GUI
+     * element found in the specification.
+     * </p>
+     */
+    private Properties mappingsFromConfiguration;
+
+    
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementFactory#instantiateGUIElement(de.ugoe.cs.autoquest
+     * .eventcore.guimodel.IGUIElementSpec, de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement)
+     */
+    @Override
+    public IGUIElement instantiateGUIElement(IGUIElementSpec specification, IGUIElement parent)
+        throws GUIModelConfigurationException
+    {
+        Properties mappings = getMappingsFromConfiguration();
+        IGUIElement guiElement = null;
+
+        String className = mappings.getProperty(specification.getType());
+        if (className != null) {
+            try {
+                Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
+
+                if (!IGUIElement.class.isAssignableFrom(clazz)) {
+                    Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                        className + " is no valid GUIElement derivate.");
+
+                    return null;
+                }
+
+                Constructor<?> constructor = null;
+                Class<?> parentClass = (parent == null) ? null : parent.getClass();
+
+                // search for a constructor, that perfectly matches the types
+                for (Constructor<?> candidate : clazz.getConstructors()) {
+                    if ((parentClass != null) && (candidate.getParameterTypes().length == 2) &&
+                        (candidate.getParameterTypes()[0].equals(specification.getClass())) &&
+                        (candidate.getParameterTypes()[1].equals(parentClass)))
+                    {
+                        constructor = candidate;
+                        break;
+                    }
+                    else if (parentClass == null) {
+                        if ((candidate.getParameterTypes().length >= 1) &&
+                            (candidate.getParameterTypes()[0].equals(specification.getClass())))
+                        {
+                            constructor = candidate;
+                            break;
+                        }
+                    }
+                }
+
+                if (constructor == null) {
+                    // search for an assignable constructor
+                    for (Constructor<?> candidate : clazz.getConstructors()) {
+                        if ((candidate.getParameterTypes().length == 2) &&
+                            (candidate.getParameterTypes()[0].isInstance(specification)) &&
+                            (candidate.getParameterTypes()[1].isInstance(parent)))
+                        {
+                            constructor = candidate;
+                            break;
+                        }
+                    }
+
+                }
+                
+                if (constructor != null) {
+                    guiElement = (IGUIElement) constructor.newInstance(specification, parent);
+                }
+                else {
+                    throw new NoSuchMethodException
+                        ("no constructor with two parameters and assignable parameter types for " +
+                         specification.getClass() + " and " +
+                         (parent != null ? parent.getClass() : "null") + " found in class " +
+                         clazz);
+                }
+
+            }
+            catch (ClassNotFoundException e) {
+                Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                                className + " can not be loaded.");
+                throw new GUIModelConfigurationException
+                    ("configured GUI element representing class " + className +
+                     " can not be loaded.", e);
+            }
+            catch (SecurityException e) {
+                Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                                className + " can not be instantiated due to security reasons.");
+                throw new GUIModelConfigurationException
+                    ("configured GUI element representing class " + className +
+                     " can not be instantiated due to security reasons.", e);
+            }
+            catch (NoSuchMethodException e) {
+                Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                                className + " does not provide an appropriate constructor.");
+                throw new GUIModelConfigurationException
+                    ("configured GUI element representing class " + className +
+                     " does not provide an appropriate constructor.", e);
+            }
+            catch (IllegalArgumentException e) {
+                Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                                className + " does not provide an appropriate constructor " +
+                                "accepting the provided parameters.");
+                throw new GUIModelConfigurationException
+                    ("configured GUI element representing class " + className + " does not " +
+                     "provide an appropriate constructor accepting the provided parameters.", e);
+            }
+            catch (InstantiationException e) {
+                Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                                className + " can not be instantiated.");
+                throw new GUIModelConfigurationException
+                    ("configured GUI element representing class " + className +
+                     " can not be instantiated.", e);
+            }
+            catch (IllegalAccessException e) {
+                Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                                className + " can not be instantiated.");
+                throw new GUIModelConfigurationException
+                    ("configured GUI element representing class " + className +
+                     " can not be instantiated.", e);
+            }
+            catch (InvocationTargetException e) {
+                Console.traceln(Level.WARNING, "configured GUI element representing class " +
+                                className + " can not be instantiated.");
+                throw new GUIModelConfigurationException
+                    ("configured GUI element representing class " + className +
+                     " can not be instantiated.", e);
+            }
+        }
+        
+        if (guiElement == null ) {
+            Console.traceln(Level.WARNING, "no class representing GUI elements of type " +
+                            specification.getType() + " found. Please extend GUI element " +
+                            "mapping files.");
+            throw new GUIModelConfigurationException
+                ("no class representing GUI elements of type " + specification.getType() +
+                 " found. Please extend GUI element mapping files");
+        }
+
+        return guiElement;
+    }
+
+    /**
+     * <p>
+     * Loads the mappings for GUI elements. All files that start with &quot;guimapping&quot;, end
+     * with &quot;.txt&quot;, and are located in the folter &quot;data/guimappings&quot; (relative
+     * to the working directory) are loaded.
+     * </p>
+     * 
+     * @return loaded GUI mappings
+     */
+    private synchronized Properties getMappingsFromConfiguration()
+        throws GUIModelConfigurationException
+    {
+        if (mappingsFromConfiguration != null) {
+            return mappingsFromConfiguration;
+        }
+        else {
+            mappingsFromConfiguration = new Properties();
+
+            File mappingsFolder = new File("data/guimappings");
+            File[] children = mappingsFolder.listFiles();
+
+            if (children != null) {
+                for (File mappingsFile : children) {
+                    if (!mappingsFile.isDirectory() &&
+                        mappingsFile.getName().startsWith("guimapping") &&
+                        mappingsFile.getName().endsWith(".txt"))
+                    {
+                        InputStream inStream = null;
+                        try {
+                            inStream = new FileInputStream(mappingsFile);
+                            mappingsFromConfiguration.load(inStream);
+                        }
+                        catch (FileNotFoundException e) {
+                            throw new GUIModelConfigurationException(
+                                                                     "could not read mapping configuration file " +
+                                                                         mappingsFile, e);
+                        }
+                        catch (IOException e) {
+                            throw new GUIModelConfigurationException(
+                                                                     "could not read mapping configuration file " +
+                                                                         mappingsFile, e);
+                        }
+                        finally {
+                            if (inStream != null) {
+                                try {
+                                    inStream.close();
+                                }
+                                catch (IOException e) {
+                                    // ignore
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            else {
+                throw new GUIModelConfigurationException(
+                                                         "no GUI mappings file provided in folder " +
+                                                             mappingsFolder);
+            }
+
+            return mappingsFromConfiguration;
+        }
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModel.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModel.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModel.java	(revision 922)
@@ -0,0 +1,585 @@
+
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * A GUI model is a tree of {@link IGUIElements} and represents a complete GUI of a software. It is
+ * platform independent. It may have several root nodes, as some GUIs are made up of several Frames
+ * being independent from each other. The GUI model is filled using the
+ * {@link #integratePath(List, IGUIElementFactory)} method.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms, Steffen Herbold
+ */
+public class GUIModel {
+
+    /**
+     * <p>
+     * The root node of the tree not provided externally.
+     * </p>
+     */
+    private TreeNode root = new TreeNode();
+
+    /**
+     * <p>
+     * A list with all nodes currently known
+     * </p>
+     */
+    private List<TreeNode> allNodes = new ArrayList<TreeNode>();
+
+    /**
+     * <p>
+     * Integrates a path of GUI elements into the GUI model. The GUI model itself is a tree and
+     * therefore a set of different paths through the tree that start with a root node and end with
+     * a leaf node. Such a path can be added to the tree. The method checks, if any of the GUI
+     * elements denoted by the path already exists. If so, it reuses it. It may therefore also
+     * return an existing GUI element being the leaf node of the provided path. If a GUI element of
+     * the path does not exist yet, it creates a new one using the provided GUI element factory.
+     * </p>
+     * <p>
+     * If a GUI element specification describes an existing GUI element or not is determined through
+     * comparing the GUI element specifications of the existing GUI elements with the ones provided
+     * in the path. The comparison is done using the
+     * {@link IGUIElementSpec#getSimilarity(IGUIElementSpec)} method. The comparison is only done on
+     * the correct levels. I.e. the currently known root elements of the tree are only compared to
+     * the first element in the path. If the correct one is found or created, its children are
+     * compared only to the second specification in the path, and so on.
+     * </p>
+     * <p>
+     * The returned GUI elements are singletons. I.e. it is tried to return always the identical
+     * object for the same denoted element. However, while creating the GUI model, the similarity of
+     * GUI elements may change. Therefore, the method might determine, that two formerly different
+     * nodes are now similar. (This may happen, e.g. if GUI elements do not have initial names which
+     * are set afterwards. Therefore, first they are handled differently and later they can be
+     * identified as being the same.) In such a case, there are already several GUI element objects
+     * instantiated for the same GUI element. The singleton paradigm gets broken. Therefore, such
+     * GUI element objects are registered with each other, so that their equal method can determine
+     * equality again correctly, although the objects are no singletons anymore.
+     * </p>
+     * 
+     * @param guiElementPath
+     *            the path to integrate into the model
+     * @param guiElementFactory
+     *            the GUI element factory to be used for instantiating GUI element objects
+     * 
+     * @return The GUI element object representing the GUI element denoted by the provided path
+     * 
+     * @throws GUIModelException
+     *             thrown in cases such as the GUI element object could not be instantiated
+     * @throws IllegalArgumentException
+     *             if the provided path is invalid.
+     */
+    public IGUIElement integratePath(List<? extends IGUIElementSpec> guiElementPath,
+                                     IGUIElementFactory guiElementFactory)
+        throws GUIModelException, IllegalArgumentException
+    {
+        if ((guiElementPath == null) || (guiElementPath.size() <= 0)) {
+            throw new IllegalArgumentException("GUI element path must contain at least one element");
+        }
+
+        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>();
+
+        for (IGUIElementSpec spec : guiElementPath) {
+            remainingPath.add(spec);
+        }
+
+        return integratePath(root, remainingPath, guiElementFactory);
+    }
+
+    /**
+     * <p>
+     * Returns all children of the provided GUI element or null, if it does not have any or the node
+     * is unknown.
+     * </p>
+     * 
+     * @param guiElement
+     *            the GUI element of which the children shall be returned
+     * 
+     * @return As described
+     */
+    public List<IGUIElement> getChildren(IGUIElement guiElement) {
+        List<IGUIElement> result = null;
+        for (TreeNode node : allNodes) {
+            if (node.guiElement.equals(guiElement)) {
+                if (result == null) {
+                    result = new ArrayList<IGUIElement>();
+
+                    if (node.children != null) {
+                        for (TreeNode child : node.children) {
+                            result.add(child.guiElement);
+                        }
+                    }
+                }
+                else {
+                    Console
+                        .traceln(Level.SEVERE,
+                                 "Multiple nodes in the internal GUI model match the same GUI element. "
+                                     + "This should not be the case and the GUI model is probably invalid.");
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>
+     * Returns the parent GUI element of the provided GUI element or null, if it does not have a
+     * parent (i.e. if it is a root node) or if the node is unknown.
+     * </p>
+     * 
+     * @param guiElement
+     *            the GUI element of which the parent shall be returned
+     * 
+     * @return As described
+     */
+    public IGUIElement getParent(IGUIElement guiElement) {
+        IGUIElement parent = null;
+
+        for (TreeNode node : allNodes) {
+            for (TreeNode child : node.children) {
+                if (child.guiElement.equals(guiElement)) {
+                    if (parent != null) {
+                        parent = node.guiElement;
+                    }
+                    else {
+                        Console
+                            .traceln(Level.SEVERE,
+                                     "Multiple nodes in the internal GUI model match the same GUI element. "
+                                         + "This should not be the case and the GUI model is probably invalid.");
+                    }
+                }
+            }
+        }
+
+        return parent;
+    }
+
+    /**
+     * <p>
+     * Returns all root GUI elements of the model or an empty list, if the model is empty
+     * </p>
+     * 
+     * @return As described
+     */
+    public List<IGUIElement> getRootElements() {
+        List<IGUIElement> roots = new ArrayList<IGUIElement>();
+
+        if (root.children != null) {
+            for (TreeNode rootChild : root.children) {
+                roots.add(rootChild.guiElement);
+            }
+        }
+
+        return roots;
+    }
+
+    /**
+     * <p>
+     * dumps the GUI model to the provided stream. Each node is represented through its toString()
+     * method. If a node has children, those are dumped indented and surrounded by braces.
+     * </p>
+     * 
+     * @param out
+     *            The stream to dump the textual representation of the model to
+     * @param encoding
+     *            The encoding to be used while dumping
+     */
+    public void dump(OutputStream out, String encoding) {
+        PrintStream stream;
+
+        if (out instanceof PrintStream) {
+            stream = (PrintStream) out;
+        }
+        else {
+            String enc = encoding == null ? "UTF-8" : encoding;
+            try {
+                stream = new PrintStream(out, true, enc);
+            }
+            catch (UnsupportedEncodingException e) {
+                throw new IllegalArgumentException("encodind " + enc + " not supported");
+            }
+        }
+
+        for (IGUIElement root : getRootElements()) {
+            dumpGUIElement(stream, root, "");
+        }
+    }
+
+    /**
+     * <p>
+     * By calling this method, the GUIModel is traversed and similar nodes are merged.
+     * </p>
+     * 
+     */
+    public void condenseModel() {
+        mergeSubTree(root);
+    }
+    
+    /**
+     * <p>
+     * Merges the tree nodes of two GUI elements. The GUI elements need to have the same parent.
+     * </p>
+     * 
+     * @param guiElement1
+     *            the first merge GUI element
+     * @param guiElement2
+     *            the second merge GUI element
+     * @throws IllegalArgumentException
+     *             thrown if the two GUI elements do not have the same parent
+     */
+    public void mergeGUIElements(IGUIElement guiElement1, IGUIElement guiElement2)
+        throws IllegalArgumentException
+    {
+        // check if both nodes have the same parent
+        IGUIElement parentElement = guiElement1.getParent();
+        if (parentElement != null && !parentElement.equals(guiElement2.getParent())) {
+            throw new IllegalArgumentException("can only merge nodes with the same parent");
+        }
+
+        // get the TreeNode of the parent of the GUI elements
+        TreeNode parent = findNode(parentElement);
+
+        // get the TreeNodes for both GUI elements
+        TreeNode node1 = findNode(guiElement1);
+        TreeNode node2 = findNode(guiElement2);
+
+        if (node1 == null || node2 == null) {
+            throw new IllegalArgumentException(
+                                               "Error while merging nodes: one element is not part of the GUI model!");
+        }
+
+        TreeNode replacement = mergeTreeNodes(node1, node2);
+
+        if (parent != null) {
+            // remove node1 and node2 from the parent's children and add the replacement instead
+            // assumes that there are no duplicates of node1 and node2
+            if (parent.children != null) {
+                parent.children.set(parent.children.indexOf(node1), replacement);
+                parent.children.remove(node2);
+            }
+        }
+
+    }
+
+    /**
+     * <p>
+     * internally integrates a path as the children of the provided parent node. This method is
+     * recursive and calls itself, for the child of the parent node, that matches the first element
+     * in the remaining path.
+     * </p>
+     * 
+     * @param parentNode
+     *            the parent node to add children for
+     * @param guiElementPath
+     *            the path of children to be created starting with the parent node
+     * @param guiElementFactory
+     *            the GUI element factory to be used for instantiating GUI element objects
+     * 
+     * @return The GUI element object representing the GUI element denoted by the provided path
+     * 
+     * @throws GUIModelException
+     *             thrown in cases such as the GUI element object could not be instantiated
+     */
+    private IGUIElement integratePath(TreeNode parentNode,
+                                      List<? extends IGUIElementSpec> remainingPath,
+                                      IGUIElementFactory guiElementFactory)
+        throws GUIModelException
+    {
+        IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0);
+
+        TreeNode child = findEqualChild(parentNode, specToIntegrateElementFor);
+        if (child == null) {
+            IGUIElement newElement =
+                guiElementFactory.instantiateGUIElement(specToIntegrateElementFor,
+                                                        parentNode.guiElement);
+
+            child = parentNode.addChild(newElement);
+        }
+
+        if (remainingPath.size() > 0) {
+            return integratePath(child, remainingPath, guiElementFactory);
+        }
+        else {
+            return child.guiElement;
+        }
+    }
+
+    /**
+     * <p>
+     * Searches the children of a tree node to see if the {@link IGUIElementSpec} of equals the
+     * specification of the {@link TreeNode#guiElement} of the child. If a match is found, the child
+     * is returned.
+     * </p>
+     * 
+     * @param parentNode
+     *            parent node whose children are searched
+     * @param specToMatch
+     *            specification that is searched for
+     * @return matching child node or null if no child matches
+     */
+    private TreeNode findEqualChild(TreeNode parentNode, IGUIElementSpec specToMatch) {
+        if (parentNode.children != null) {
+            for (TreeNode child : parentNode.children) {
+                if (specToMatch.equals(child.guiElement.getSpecification())) {
+                    return child;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * <p>
+     * Merges all similar nodes in the sub-tree of the GUI model defined by the subTreeRoot.
+     * </p>
+     * <p>
+     * The merging order is a bottom-up. This means, that we first call mergeSubTree recursively for
+     * the grand children of the subTreeRoot, before we merge subTreeRoot.
+     * </p>
+     * <p>
+     * The merging strategy is top-down. This means, that every time we merge two child nodes, we
+     * call mergeSubTree recursively for all children of the merged nodes in order to check if we
+     * can merge the children, too.
+     * </p>
+     * 
+     * @param subTreeRoot
+     *            root node of the sub-tree that is merged
+     */
+    private void mergeSubTree(TreeNode subTreeRoot) {
+        if (subTreeRoot.children == null || subTreeRoot.children.isEmpty()) {
+            return;
+        }
+
+        // lets first merge the grand children of the parentNode
+        for (TreeNode child : subTreeRoot.children) {
+            mergeSubTree(child);
+        }
+
+        boolean performedMerge;
+
+        do {
+            performedMerge = false;
+            for (int i = 0; !performedMerge && i < subTreeRoot.children.size(); i++) {
+                IGUIElementSpec elemSpec1 =
+                    subTreeRoot.children.get(i).guiElement.getSpecification();
+                for (int j = i + 1; !performedMerge && j < subTreeRoot.children.size(); j++) {
+                    IGUIElementSpec elemSpec2 =
+                        subTreeRoot.children.get(j).guiElement.getSpecification();
+                    if (elemSpec1.getSimilarity(elemSpec2)) {
+                        TreeNode replacement =
+                            mergeTreeNodes(subTreeRoot.children.get(i), subTreeRoot.children.get(j));
+
+                        subTreeRoot.children.set(i, replacement);
+                        subTreeRoot.children.remove(j);
+                        performedMerge = true;
+                        i--;
+                        break;
+                    }
+                }
+            }
+        }
+        while (performedMerge);
+    }
+
+    /**
+     * <p>
+     * merges two nodes with each other. Merging means registering the GUI element objects with each
+     * other for equality checks. Further it add all children of both nodes to a new replacing node.
+     * Afterwards, all similar nodes of the replacement node are merged as well.
+     * </p>
+     * 
+     * @param treeNode1
+     *            the first of the two nodes to be merged
+     * @param treeNode2
+     *            the second of the two nodes to be merged
+     * @return a tree node being the merge of the two provided nodes.
+     */
+    private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) {
+        // the following two lines are needed to preserve the references to the existing GUI
+        // elements. If two elements are the same, one should be deleted to make the elements
+        // singletons again. However, there may exist references to both objects. To preserve
+        // these, we simply register the equal GUI elements with each other so that an equals
+        // check can return true.
+        treeNode1.guiElement.addEqualGUIElement(treeNode2.guiElement);
+        treeNode2.guiElement.addEqualGUIElement(treeNode1.guiElement);
+
+        // and now a replacement node that is the merge of treeNode1 and treeNode2 is created
+        TreeNode replacement = new TreeNode();
+        replacement.guiElement = treeNode1.guiElement;
+        if (treeNode1.children != null) {
+            for (TreeNode child : treeNode1.children) {
+                replacement.addChildNode(child);
+            }
+        }
+        if (treeNode2.children != null) {
+            for (TreeNode child : treeNode2.children) {
+                replacement.addChildNode(child);
+            }
+        }
+
+        mergeSubTree(replacement);
+
+        replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification());
+
+        // finally, update the known nodes list
+        // if you don't do this getChildren will return wrong things and very bad things happen!
+        allNodes.remove(treeNode1);
+        allNodes.remove(treeNode2);
+        allNodes.add(replacement);
+
+        return replacement;
+    }
+
+    /**
+     * <p>
+     * dumps a GUI element to the stream. A dump contains the toString() representation of the GUI
+     * element as well as a indented list of its children surrounded by braces.
+     * </p>
+     * 
+     * @param out
+     *            {@link PrintStream} where the guiElement is dumped to
+     * @param guiElement
+     *            the guiElement whos string represenation is dumped
+     * @param indent
+     *            indent string of the dumping
+     */
+    private void dumpGUIElement(PrintStream out, IGUIElement guiElement, String indent) {
+        out.print(indent);
+        out.print(guiElement);
+
+        List<IGUIElement> children = getChildren(guiElement);
+
+        if ((children != null) && (children.size() > 0)) {
+            out.println(" {");
+
+            for (IGUIElement child : children) {
+                dumpGUIElement(out, child, indent + "  ");
+            }
+
+            out.print(indent);
+            out.print("}");
+        }
+
+        out.println();
+    }
+    
+    /**
+     * <p>
+     * Retrieves the TreeNode associated with a GUI element. Returns null if no such TreeNode is
+     * found.
+     * </p>
+     * 
+     * @param element
+     *            the GUI element
+     * @return associated TreeNode; null if no such node exists
+     */
+    private TreeNode findNode(IGUIElement element) {
+        if (element == null) {
+            return null;
+        }
+
+        TreeNode result = null;
+        for (TreeNode node : allNodes) {
+            if (node.guiElement.equals(element)) {
+                if (result == null) {
+                    result = node;
+                }
+                else {
+                    Console
+                        .traceln(Level.SEVERE,
+                                 "Multiple nodes in the internal GUI model match the same GUI element. "
+                                     + "This should not be the case and the GUI model is probably invalid.");
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Used internally for building up the tree of GUI elements.
+     * </p>
+     * 
+     * @version 1.0
+     * @author Patrick Harms, Steffen Herbold
+     */
+    private class TreeNode {
+
+        /**
+         * <p>
+         * GUI element associated with the TreeNode.
+         * </p>
+         */
+        private IGUIElement guiElement;
+
+        /**
+         * <p>
+         * Children of the TreeNode.
+         * </p>
+         */
+        private List<TreeNode> children;
+
+        /**
+         * <p>
+         * Adds a child to the current node while keeping all lists of nodes up to date
+         * </p>
+         * 
+         * @param guiElement
+         *            GUI element that will be associated with the new child
+         * @return the added child
+         */
+        private TreeNode addChild(IGUIElement guiElement) {
+            if (children == null) {
+                children = new ArrayList<TreeNode>();
+            }
+
+            TreeNode child = new TreeNode();
+            child.guiElement = guiElement;
+            children.add(child);
+
+            allNodes.add(child);
+
+            return child;
+        }
+
+        /**
+         * 
+         * <p>
+         * Adds a TreeNode as child to the current node. This way, the whole sub-tree is added.
+         * </p>
+         * 
+         * @param node
+         *            child node that is added
+         * @return node that has been added
+         */
+        private TreeNode addChildNode(TreeNode node) {
+            if (children == null) {
+                children = new ArrayList<TreeNode>();
+            }
+            children.add(node);
+            return node;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return guiElement.toString();
+        }
+    }
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModelConfigurationException.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModelConfigurationException.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModelConfigurationException.java	(revision 922)
@@ -0,0 +1,69 @@
+
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Exception that is thrown if there is a failure during the creation of a {@link IGUIElement} by
+ * the {@link GUIElementFactory}.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class GUIModelConfigurationException extends GUIModelException {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelConfigurationException.
+     * </p>
+     */
+    public GUIModelConfigurationException() {
+        super();
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelConfigurationException.
+     * </p>
+     * 
+     * @param message
+     *            message of the exception
+     */
+    public GUIModelConfigurationException(String message) {
+        super(message);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelConfigurationException.
+     * </p>
+     * 
+     * @param cause
+     *            cause of the exception
+     */
+    public GUIModelConfigurationException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelConfigurationException.
+     * </p>
+     * 
+     * @param message
+     *            message of the exception
+     * @param cause
+     *            cause of the exception
+     */
+    public GUIModelConfigurationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModelException.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModelException.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModelException.java	(revision 922)
@@ -0,0 +1,68 @@
+
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Exception that is thrown if there are problems with the {@link GUIModel}.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class GUIModelException extends Exception {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelException.
+     * </p>
+     */
+    public GUIModelException() {
+        super();
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelException.
+     * </p>
+     * 
+     * @param message
+     *            message of the exception
+     */
+    public GUIModelException(String message) {
+        super(message);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelException.
+     * </p>
+     * 
+     * @param cause
+     *            cause of the exception
+     */
+    public GUIModelException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new GUIModelException.
+     * </p>
+     * 
+     * @param message
+     *            message of the exception
+     * @param cause
+     *            cause of the exception
+     */
+    public GUIModelException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IButton.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IButton.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IButton.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a button
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IButton extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ICanvas.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ICanvas.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ICanvas.java	(revision 922)
@@ -0,0 +1,14 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a canvas, i.e. something to display data or
+ * media
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ICanvas extends IGUIElement {
+    
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ICheckBox.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ICheckBox.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ICheckBox.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a check box
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ICheckBox extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IComboBox.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IComboBox.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IComboBox.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a combo box
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IComboBox extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IDialog.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IDialog.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IDialog.java	(revision 922)
@@ -0,0 +1,14 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a dialog
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IDialog extends IGUIElement
+{
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IFrame.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IFrame.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IFrame.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a frame or window
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IFrame extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElement.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElement.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElement.java	(revision 922)
@@ -0,0 +1,75 @@
+
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+
+/**
+ * <p>
+ * Common interface for all GUI elements.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IGUIElement extends IEventTarget {
+
+    /**
+     * <p>
+     * Returns the specification of the GUI element.
+     * </p>
+     * 
+     * @return the specification
+     */
+    public IGUIElementSpec getSpecification();
+
+    /**
+     * <p>
+     * Returns the parent of the GUI element.
+     * </p>
+     * 
+     * @return the parent
+     */
+    public IGUIElement getParent();
+
+    /**
+     * <p>
+     * Defines that {@link IGUIElement} implementations have to define equals.
+     * </p>
+     * 
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object other);
+
+    /**
+     * <p>
+     * Defines that {@link IGUIElement} implementations have to define hashCode.
+     * </p>
+     * 
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode();
+
+    /**
+     * <p>
+     * Updates the specification of a GUI element with another specification, e.g., to add further
+     * known names of the GUI element.
+     * </p>
+     * 
+     * @param furtherSpec
+     *            additional specification
+     */
+    public void updateSpecification(IGUIElementSpec furtherSpec);
+
+    /**
+     * <p>
+     * The {@link IGUIElement} that is passed by this function is equal to the current GUI element
+     * and will hereafter be treated as such.
+     * </p>
+     * 
+     * @param guiElement
+     *            GUI element that is equal
+     */
+    public void addEqualGUIElement(IGUIElement equalElement);
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElementFactory.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElementFactory.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElementFactory.java	(revision 922)
@@ -0,0 +1,29 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Common interface for GUI element factories. 
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IGUIElementFactory {
+
+    /**
+     * <p>
+     * Instantiates a new {@link IGUIElement} from a given specification.
+     * </p>
+     * 
+     * @param specification
+     *            specification of the new GUI element
+     * @param parent
+     *            parent of the new GUI element
+     * @return created GUI element
+     * @throws GUIModelConfigurationException
+     *             thrown if there is a problem during the creation of the GUI element
+     */
+    public IGUIElement instantiateGUIElement(IGUIElementSpec specification, IGUIElement parent)
+        throws GUIModelConfigurationException;
+    
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElementSpec.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElementSpec.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IGUIElementSpec.java	(revision 922)
@@ -0,0 +1,56 @@
+
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * Common interface for GUI element specifications.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IGUIElementSpec extends Serializable {
+
+    /**
+     * <p>
+     * Returns a string represenation of the GUI element type that this specification represents.
+     * </p>
+     * 
+     * @return
+     */
+    public String getType();
+
+    /**
+     * <p>
+     * Evaluates if two GUI specifications are similar. Similar means that a heuristic determines
+     * that the two GUI specifications describe the same GUI element.
+     * </p>
+     * 
+     * @param other
+     *            specification whose similarity to this is evaluated
+     * @return true if the specifications are similar; false otherwise
+     */
+    public boolean getSimilarity(IGUIElementSpec other);
+
+    /**
+     * <p>
+     * Defines that {@link IGUIElement} implementations have to define equals.
+     * </p>
+     * 
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object other);
+
+    /**
+     * <p>
+     * Defines that {@link IGUIElement} implementations have to define hashCode.
+     * </p>
+     * 
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode();
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IListBox.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IListBox.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IListBox.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a list or list box
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IListBox extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenu.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenu.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenu.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a menu
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IMenu extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenuBar.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenuBar.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenuBar.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a menu bar
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IMenuBar extends IMenu {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenuButton.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenuButton.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IMenuButton.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a button in a menu
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IMenuButton extends IButton {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IPanel.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IPanel.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IPanel.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a panel for structuring GUI content
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IPanel extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IRadioButton.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IRadioButton.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IRadioButton.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a radio button
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IRadioButton extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IScrollBar.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IScrollBar.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IScrollBar.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a scroll bar of a scroll pane
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IScrollBar extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IScrollPane.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IScrollPane.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IScrollPane.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a scroll pane
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IScrollPane extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IShape.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IShape.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IShape.java	(revision 922)
@@ -0,0 +1,14 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent any kind of shape not covered by the other
+ * marker interfaces
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IShape extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ISplitPane.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ISplitPane.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ISplitPane.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a split pane
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ISplitPane extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITabbedPane.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITabbedPane.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITabbedPane.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a tabbed pane
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ITabbedPane extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITable.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITable.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITable.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a table
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ITable extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITextArea.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITextArea.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITextArea.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a multi line text area
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ITextArea extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITextField.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITextField.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITextField.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a single line text field
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ITextField extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IToolBar.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IToolBar.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/IToolBar.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a tool bar
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface IToolBar extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITrackBar.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITrackBar.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITrackBar.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a track bar
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ITrackBar extends IGUIElement {
+
+}
Index: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITree.java
===================================================================
--- trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITree.java	(revision 922)
+++ trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/ITree.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.eventcore.guimodel;
+
+/**
+ * <p>
+ * Marker interface for GUI elements that represent a tree view
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public interface ITree extends IGUIElement {
+
+}
Index: trunk/autoquest-core-tasktrees-test/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-core-tasktrees-test/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-core-tasktrees-test/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/manager/TaskTreeManagerTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/manager/TaskTreeManagerTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/manager/TaskTreeManagerTest.java	(revision 922)
@@ -0,0 +1,471 @@
+package de.ugoe.cs.autoquest.tasktrees.manager;
+
+import java.util.logging.Level;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.tasktrees.TaskTreeChecker;
+import de.ugoe.cs.autoquest.tasktrees.manager.ComponentManager;
+import de.ugoe.cs.autoquest.tasktrees.manager.TaskTreeManager;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class TaskTreeManagerTest {
+    
+    /** */
+    TaskTreeManager manager;
+
+    /**
+     *
+     */
+    @Before
+    public void setUp() {
+        new TextConsole(Level.FINEST);
+        manager = new TaskTreeManager();
+    }
+
+    /**
+     *
+     */
+    @After
+    public void tearDown() {
+        manager = null;
+        ComponentManager.clearInstance();
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testOneEventOnOneElement() {
+        simulateEvent(new DummyInteraction("bla", 1), new DummyGUIElement("elem1"));
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence {" +
+             "  Event bla {}" +
+             "}", manager.getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testManyEventsOnOneElement() {
+        IEventTarget eventTarget = new DummyGUIElement("elem1");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget);
+        simulateEvent(new DummyInteraction("blu", 1), eventTarget);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence {" +
+             "  Event bla {}" +
+             "  Event bli {}" +
+             "  Event blo {}" +
+             "  Event blu {}" +
+             "  Event bla {}" +
+             "}", manager.getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testOneEventOnManyElements() {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        IEventTarget eventTarget3 = new DummyGUIElement("elem3");
+        IEventTarget eventTarget4 = new DummyGUIElement("elem4");
+        IEventTarget eventTarget5 = new DummyGUIElement("elem5");
+        IEventTarget eventTarget6 = new DummyGUIElement("elem6");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget3);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget5);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget6);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Sequence sequence1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence2 {" +
+             "    Event bli {}" +
+             "  }" +
+             "  Sequence sequence3 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence4 {" +
+             "    Event bli {}" +
+             "  }" +
+             "  Sequence sequence5 {" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence6 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", manager.getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testManyEventsOnManyElements() {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        IEventTarget eventTarget3 = new DummyGUIElement("elem3");
+        IEventTarget eventTarget4 = new DummyGUIElement("elem4");
+        IEventTarget eventTarget5 = new DummyGUIElement("elem5");
+        IEventTarget eventTarget6 = new DummyGUIElement("elem6");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget3);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget5);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget6);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Sequence sequence1 {" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence2 {" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence3 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence4 {" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence5 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence6 {" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "}", manager.getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testEventIterationDetection() throws Exception {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventType event1 = new DummyInteraction("bla", 1);
+        simulateEvent(event1, eventTarget1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Event bla {}" +
+             "}", manager.getTaskTree());
+
+        simulateEvent(event1, eventTarget1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", manager.getTaskTree());
+
+        simulateEvent(event1, eventTarget1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", manager.getTaskTree());
+
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, eventTarget1);
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", manager.getTaskTree());
+
+        // now test with preceding and trailing other interactions
+        IEventType event2 = new DummyInteraction("bli", 1);
+        IEventType event3 = new DummyInteraction("blup", 1);
+
+        simulateEvent(event2, eventTarget1);
+        simulateEvent(event3, eventTarget1);
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, eventTarget1);
+        }
+        simulateEvent(event3, eventTarget1);
+        simulateEvent(event2, eventTarget1);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event bli {}" +
+             "  Event blup {}" +
+             "  Iteration iteration2 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event blup {}" +
+             "  Event bli {}" +
+             "}", manager.getTaskTree());
+
+        // now test with iterations of iterations
+
+        for (int i = 0; i < 10; i++) {
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event1, eventTarget1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event2, eventTarget1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event3, eventTarget1);
+            }
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Iteration iteration0 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event bli {}" +
+             "  Event blup {}" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event blup {}" +
+             "  Event bli {}" +
+             "  Iteration iteration2 {" +
+             "    Sequence sequence1 {" +
+             "      Iteration iteration3 {" +
+             "        Event bla {}" +
+             "      }" +
+             "      Iteration iteration4 {" +
+             "        Event bli {}" +
+             "      }" +
+             "      Iteration iteration5 {" +
+             "        Event blup {}" +
+             "      }" +
+             "    }" +
+             "  }" +
+             "}", manager.getTaskTree());
+
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testSequenceIterationDetection() throws Exception {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventType event1 = new DummyInteraction("bla", 1);
+        IEventType event2 = new DummyInteraction("bli", 1);
+        IEventType event3 = new DummyInteraction("blup", 1);
+        simulateEvent(event1, eventTarget1);
+        simulateEvent(event2, eventTarget1);
+        simulateEvent(event3, eventTarget1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Event bla {}" +
+             "  Event bli {}" +
+             "  Event blup {}" +
+             "}", manager.getTaskTree());
+
+        simulateEvent(event1, eventTarget1);
+        simulateEvent(event2, eventTarget1);
+        simulateEvent(event3, eventTarget1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "}", manager.getTaskTree());
+
+        simulateEvent(event1, eventTarget1);
+        simulateEvent(event2, eventTarget1);
+        simulateEvent(event3, eventTarget1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "}", manager.getTaskTree());
+
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, eventTarget1);
+            simulateEvent(event2, eventTarget1);
+            simulateEvent(event3, eventTarget1);
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "}", manager.getTaskTree());
+
+        // now test with preceding and trailing other interactions
+        IEventType event4 = new DummyInteraction("ble", 1);
+        IEventType event5 = new DummyInteraction("blo", 1);
+        IEventType event6 = new DummyInteraction("blu", 1);
+        simulateEvent(event4, eventTarget1);
+        simulateEvent(event5, eventTarget1);
+        simulateEvent(event6, eventTarget1);
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, eventTarget1);
+            simulateEvent(event2, eventTarget1);
+            simulateEvent(event3, eventTarget1);
+        }
+        simulateEvent(event6, eventTarget1);
+        simulateEvent(event5, eventTarget1);
+        simulateEvent(event4, eventTarget1);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event ble {}" +
+             "  Event blo {}" +
+             "  Event blu {}" +
+             "  Iteration iteration2 {" +
+             "    Sequence sequence3 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event blu {}" +
+             "  Event blo {}" +
+             "  Event ble {}" +
+             "}", manager.getTaskTree());
+
+        // now test with iterations of iterations
+        for (int i = 0; i < 10; i++) {
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event1, eventTarget1);
+                simulateEvent(event2, eventTarget1);
+                simulateEvent(event3, eventTarget1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event2, eventTarget1);
+                simulateEvent(event1, eventTarget1);
+                simulateEvent(event3, eventTarget1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event1, eventTarget1);
+                simulateEvent(event2, eventTarget1);
+                simulateEvent(event3, eventTarget1);
+            }
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event ble {}" +
+             "  Event blo {}" +
+             "  Event blu {}" +
+             "  Iteration iteration2 {" +
+             "    Sequence sequence3 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event blu {}" +
+             "  Event blo {}" +
+             "  Event ble {}" +
+             "  Iteration iteration3 {" +
+             "    Sequence sequence4 {" +
+             "      Iteration iteration4 {" +
+             "        Sequence sequence4 {" +
+             "          Event bla {}" +
+             "          Event bli {}" +
+             "          Event blup {}" +
+             "        }" +
+             "      }" +
+             "      Iteration iteration5 {" +
+             "        Sequence sequence5 {" +
+             "          Event bli {}" +
+             "          Event bla {}" +
+             "          Event blup {}" +
+             "        }" +
+             "      }" +
+             "      Iteration iteration6 {" +
+             "        Sequence sequence6 {" +
+             "          Event bla {}" +
+             "          Event bli {}" +
+             "          Event blup {}" +
+             "        }" +
+             "      }" +
+             "    }" +
+             "  }" +
+             "}", manager.getTaskTree());
+    }
+
+    /**
+     *
+     */
+    private void simulateEvent(IEventType eventType, IEventTarget eventTarget) {
+        manager.handleNewEvent(new Event(eventType, eventTarget));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRuleTest.java	(revision 922)
@@ -0,0 +1,68 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.EventTaskComparisonRule;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+
+/**
+ * @author Patrick Harms
+ */
+public class EventTaskComparisonRuleTest {
+
+    /**
+     *
+     */
+    @Test
+    public void test() {
+        ITaskTreeNodeFactory treeNodeFactory = new TaskTreeNodeFactory();
+        
+        EventTaskComparisonRule rule = new EventTaskComparisonRule();
+        
+        // test the identity check
+        IEventType eventType1 = new StringEventType("eventType1");
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        ITaskTreeNode task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        
+        assertEquals(NodeEquality.IDENTICAL, rule.compare(task1, task1));
+
+        ITaskTreeNode task2 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task2, task1));
+        
+        IEventType eventType2 = new StringEventType("eventType2");
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget1);
+
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+        
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        task2 = treeNodeFactory.createNewEventTask(eventType1, eventTarget2);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+        
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget2);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+
+        ISelection selection = treeNodeFactory.createNewSelection();
+        assertNull(rule.compare(task1, selection));
+        assertNull(rule.compare(selection, task1));
+        assertNull(rule.compare(task2, selection));
+        assertNull(rule.compare(selection, task2));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRuleTest.java	(revision 922)
@@ -0,0 +1,133 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.GUIEventTaskComparisonRule;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+
+/**
+ * @author Patrick Harms
+ */
+public class GUIEventTaskComparisonRuleTest {
+
+    /**
+     *
+     */
+    @Test
+    public void test() {
+        ITaskTreeNodeFactory treeNodeFactory = new TaskTreeNodeFactory();
+        
+        GUIEventTaskComparisonRule rule = new GUIEventTaskComparisonRule();
+        
+        // test the identity check
+        IEventType eventType1 = new StringEventType("blub");
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        ITaskTreeNode task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        
+        assertNull(rule.compare(task1, task1));
+
+        eventType1 = new KeyboardFocusChange();
+        task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        
+        assertEquals(NodeEquality.IDENTICAL, rule.compare(task1, task1));
+
+        // test lexical equality for interaction events which are no value selections or text inputs
+        IEventType eventType2 = new KeyboardFocusChange();
+        ITaskTreeNode task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task2, task1));
+        
+        // test equality of value selections
+        eventType1 = new ValueSelection<String>("value1");
+        task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+        
+        eventType2 = new ValueSelection<String>("value1");
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget1);
+        
+        assertEquals(NodeEquality.SYNTACTICALLY_EQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.SYNTACTICALLY_EQUAL, rule.compare(task2, task1));
+        
+        eventType2 = new ValueSelection<String>("value2");
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget1);
+        
+        assertEquals(NodeEquality.SEMANTICALLY_EQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.SEMANTICALLY_EQUAL, rule.compare(task2, task1));
+        
+        // test equality of text inputs
+        List<Event> textInputEvents1 = new ArrayList<Event>();
+        textInputEvents1.add(new Event(eventType1, eventTarget1));
+        eventType1 = new TextInput("enteredText1", textInputEvents1);
+        task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+        
+        eventType2 = new TextInput("enteredText1", textInputEvents1);
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task2, task1));
+        
+        List<Event> textInputEvents2 = new ArrayList<Event>();
+        textInputEvents2.add(new Event(eventType2, eventTarget1));
+        eventType2 = new TextInput("enteredText1", textInputEvents2);
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget1);
+        
+        assertEquals(NodeEquality.SYNTACTICALLY_EQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.SYNTACTICALLY_EQUAL, rule.compare(task2, task1));
+        
+        eventType2 = new TextInput("enteredText2", textInputEvents2);
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget1);
+        
+        assertEquals(NodeEquality.SEMANTICALLY_EQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.SEMANTICALLY_EQUAL, rule.compare(task2, task1));
+        
+        // now ensure unequality for all combinations, if the event targets do not match
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        eventType1 = new KeyboardFocusChange();
+        task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        task2 = treeNodeFactory.createNewEventTask(eventType2, eventTarget2);
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+        
+        eventType1 = new ValueSelection<String>("value1");
+        task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        task2 = treeNodeFactory.createNewEventTask(eventType1, eventTarget2);
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+        
+        eventType1 = new TextInput("enteredText1", textInputEvents1);
+        task1 = treeNodeFactory.createNewEventTask(eventType1, eventTarget1);
+        task2 = treeNodeFactory.createNewEventTask(eventType1, eventTarget2);
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task1, task2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(task2, task1));
+
+        ISelection selection = treeNodeFactory.createNewSelection();
+        assertNull(rule.compare(task1, selection));
+        assertNull(rule.compare(selection, task1));
+        assertNull(rule.compare(task2, selection));
+        assertNull(rule.compare(selection, task2));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRuleTest.java	(revision 922)
@@ -0,0 +1,95 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.IterationComparisonRule;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+
+/**
+ * @author Patrick Harms
+ */
+public class IterationComparisonRuleTest {
+
+    /**
+     *
+     */
+    @Test
+    public void test() {
+        NodeEqualityRuleManager manager = new NodeEqualityRuleManager();
+        manager.init();
+        
+        ITaskTreeNodeFactory treeNodeFactory = new TaskTreeNodeFactory();
+        ITaskTreeBuilder treeBuilder = new TaskTreeBuilder();
+        
+        IterationComparisonRule rule = new IterationComparisonRule(manager);
+        
+        ITaskTreeNode task1 = new TaskTreeNode("task1");
+        ITaskTreeNode task2 = new TaskTreeNode("task2");
+        
+        assertNull(rule.compare(task1, task2));
+        
+        IIteration iteration1 = treeNodeFactory.createNewIteration();
+        assertEquals(NodeEquality.IDENTICAL, rule.compare(iteration1, iteration1));
+
+        IIteration iteration2 = treeNodeFactory.createNewIteration();
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration1, iteration2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration2, iteration1));
+        
+        treeBuilder.setChild(iteration1, task1);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(iteration1, iteration2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(iteration2, iteration1));
+        
+        treeBuilder.setChild(iteration2, task1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration1, iteration2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration2, iteration1));
+        
+        treeBuilder.setChild(iteration1, task2);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(iteration1, iteration2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(iteration2, iteration1));
+        
+        treeBuilder.setChild(iteration2, task2);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration1, iteration2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration2, iteration1));
+        
+        ISelection selection1 = treeNodeFactory.createNewSelection();
+        treeBuilder.addChild(selection1, task2);
+        treeBuilder.setChild(iteration1, selection1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration1, iteration2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration2, iteration1));
+        
+        ISelection selection2 = treeNodeFactory.createNewSelection();
+        treeBuilder.addChild(selection2, task2);
+        treeBuilder.setChild(iteration2, selection2);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration1, iteration2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration2, iteration1));
+        
+        assertNull(rule.compare(iteration1, selection1));
+        assertNull(rule.compare(selection1, iteration1));
+        assertNull(rule.compare(iteration2, selection1));
+        assertNull(rule.compare(selection1, iteration2));
+
+        assertNull(rule.compare(iteration1, selection2));
+        assertNull(rule.compare(selection2, iteration1));
+        assertNull(rule.compare(iteration2, selection2));
+        assertNull(rule.compare(selection2, iteration2));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRuleTest.java	(revision 922)
@@ -0,0 +1,59 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeAndIterationComparisonRule;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+
+/**
+ * @author Patrick Harms
+ */
+public class NodeAndIterationComparisonRuleTest {
+
+    /**
+     *
+     */
+    @Test
+    public void test() {
+        NodeEqualityRuleManager manager = new NodeEqualityRuleManager();
+        manager.init();
+        
+        ITaskTreeNodeFactory treeNodeFactory = new TaskTreeNodeFactory();
+        ITaskTreeBuilder treeBuilder = new TaskTreeBuilder();
+        
+        NodeAndIterationComparisonRule rule = new NodeAndIterationComparisonRule(manager);
+        
+        ITaskTreeNode task1 = new TaskTreeNode("task1");
+        
+        assertNull(rule.compare(task1, task1));
+        
+        IIteration iteration1 = treeNodeFactory.createNewIteration();
+        assertNull(rule.compare(iteration1, iteration1));
+        assertNull(rule.compare(task1, iteration1));
+        assertNull(rule.compare(iteration1, task1));
+
+        treeBuilder.setChild(iteration1, task1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration1, task1));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task1, iteration1));
+        
+        ISelection selection1 = treeNodeFactory.createNewSelection();
+        treeBuilder.addChild(selection1, task1);
+        treeBuilder.setChild(iteration1, selection1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(iteration1, task1));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task1, iteration1));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRuleTest.java	(revision 922)
@@ -0,0 +1,59 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeAndSelectionComparisonRule;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+
+/**
+ * @author Patrick Harms
+ */
+public class NodeAndSelectionComparisonRuleTest {
+
+    /**
+     *
+     */
+    @Test
+    public void test() {
+        NodeEqualityRuleManager manager = new NodeEqualityRuleManager();
+        manager.init();
+        
+        ITaskTreeNodeFactory treeNodeFactory = new TaskTreeNodeFactory();
+        ITaskTreeBuilder treeBuilder = new TaskTreeBuilder();
+        
+        NodeAndSelectionComparisonRule rule = new NodeAndSelectionComparisonRule(manager);
+        
+        ITaskTreeNode task1 = new TaskTreeNode("task1");
+        
+        assertNull(rule.compare(task1, task1));
+        
+        ISelection selection1 = treeNodeFactory.createNewSelection();
+        assertNull(rule.compare(selection1, selection1));
+        assertNull(rule.compare(task1, selection1));
+        assertNull(rule.compare(selection1, task1));
+
+        treeBuilder.addChild(selection1, task1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection1, task1));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task1, selection1));
+        
+        selection1 = treeNodeFactory.createNewSelection();
+        ISelection selection2 = treeNodeFactory.createNewSelection();
+        treeBuilder.addChild(selection2, task1);
+        treeBuilder.addChild(selection1, selection2);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection1, task1));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(task1, selection1));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityTest.java	(revision 922)
@@ -0,0 +1,50 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+
+/**
+ * @author Patrick Harms
+ */
+public class NodeEqualityTest {
+
+    /**
+     *
+     */
+    @Test
+    public void test() {
+        assertTrue(NodeEquality.IDENTICAL.isAtLeast(NodeEquality.IDENTICAL));
+        assertTrue(NodeEquality.IDENTICAL.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+        assertTrue(NodeEquality.IDENTICAL.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+        assertTrue(NodeEquality.IDENTICAL.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+        assertFalse(NodeEquality.IDENTICAL.isAtLeast(NodeEquality.UNEQUAL));
+
+        assertFalse(NodeEquality.LEXICALLY_EQUAL.isAtLeast(NodeEquality.IDENTICAL));
+        assertTrue(NodeEquality.LEXICALLY_EQUAL.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+        assertTrue(NodeEquality.LEXICALLY_EQUAL.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+        assertTrue(NodeEquality.LEXICALLY_EQUAL.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+        assertFalse(NodeEquality.LEXICALLY_EQUAL.isAtLeast(NodeEquality.UNEQUAL));
+
+        assertFalse(NodeEquality.SYNTACTICALLY_EQUAL.isAtLeast(NodeEquality.IDENTICAL));
+        assertFalse(NodeEquality.SYNTACTICALLY_EQUAL.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+        assertTrue(NodeEquality.SYNTACTICALLY_EQUAL.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+        assertTrue(NodeEquality.SYNTACTICALLY_EQUAL.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+        assertFalse(NodeEquality.SYNTACTICALLY_EQUAL.isAtLeast(NodeEquality.UNEQUAL));
+
+        assertFalse(NodeEquality.SEMANTICALLY_EQUAL.isAtLeast(NodeEquality.IDENTICAL));
+        assertFalse(NodeEquality.SEMANTICALLY_EQUAL.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+        assertFalse(NodeEquality.SEMANTICALLY_EQUAL.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+        assertTrue(NodeEquality.SEMANTICALLY_EQUAL.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+        assertFalse(NodeEquality.SEMANTICALLY_EQUAL.isAtLeast(NodeEquality.UNEQUAL));
+
+        assertFalse(NodeEquality.UNEQUAL.isAtLeast(NodeEquality.IDENTICAL));
+        assertFalse(NodeEquality.UNEQUAL.isAtLeast(NodeEquality.LEXICALLY_EQUAL));
+        assertFalse(NodeEquality.UNEQUAL.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL));
+        assertFalse(NodeEquality.UNEQUAL.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL));
+        assertTrue(NodeEquality.UNEQUAL.isAtLeast(NodeEquality.UNEQUAL));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRuleTest.java	(revision 922)
@@ -0,0 +1,89 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.SelectionComparisonRule;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+
+/**
+ * @author Patrick Harms
+ */
+public class SelectionComparisonRuleTest {
+
+    /**
+     * 
+     */
+    @Test
+    public void test() {
+        NodeEqualityRuleManager manager = new NodeEqualityRuleManager();
+        manager.init();
+        
+        ITaskTreeNodeFactory treeNodeFactory = new TaskTreeNodeFactory();
+        ITaskTreeBuilder treeBuilder = new TaskTreeBuilder();
+        
+        SelectionComparisonRule rule = new SelectionComparisonRule(manager);
+        
+        ITaskTreeNode task1 = new TaskTreeNode("task1");
+        ITaskTreeNode task2 = new TaskTreeNode("task2");
+        
+        assertNull(rule.compare(task1, task2));
+        
+        ISelection selection1 = treeNodeFactory.createNewSelection();
+        assertEquals(NodeEquality.IDENTICAL, rule.compare(selection1, selection1));
+
+        ISelection selection2 = treeNodeFactory.createNewSelection();
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection1, selection2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection2, selection1));
+        
+        treeBuilder.addChild(selection1, task1);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(selection1, selection2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(selection2, selection1));
+        
+        treeBuilder.addChild(selection2, task1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection1, selection2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection2, selection1));
+        
+        treeBuilder.addChild(selection1, task2);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(selection1, selection2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(selection2, selection1));
+        
+        treeBuilder.addChild(selection2, task2);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection1, selection2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection2, selection1));
+        
+        ISelection selection3 = treeNodeFactory.createNewSelection();
+        treeBuilder.addChild(selection3, task2);
+        treeBuilder.addChild(selection3, task1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection1, selection3));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection3, selection1));
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection2, selection3));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(selection3, selection2));
+
+        ISequence sequence = treeNodeFactory.createNewSequence();
+        assertNull(rule.compare(selection1, sequence));
+        assertNull(rule.compare(sequence, selection1));
+        assertNull(rule.compare(selection2, sequence));
+        assertNull(rule.compare(sequence, selection2));
+        assertNull(rule.compare(selection3, sequence));
+        assertNull(rule.compare(sequence, selection3));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRuleTest.java	(revision 922)
@@ -0,0 +1,88 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.SequenceComparisonRule;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+
+/**
+ * @author Patrick Harms
+ */
+public class SequenceComparisonRuleTest {
+
+    /**
+     * 
+     */
+    @Test
+    public void test() {
+        NodeEqualityRuleManager manager = new NodeEqualityRuleManager();
+        manager.init();
+        
+        ITaskTreeNodeFactory treeNodeFactory = new TaskTreeNodeFactory();
+        ITaskTreeBuilder treeBuilder = new TaskTreeBuilder();
+        
+        SequenceComparisonRule rule = new SequenceComparisonRule(manager);
+        
+        ITaskTreeNode task1 = new TaskTreeNode("task1");
+        ITaskTreeNode task2 = new TaskTreeNode("task2");
+        
+        assertNull(rule.compare(task1, task2));
+        
+        ISequence sequence1 = treeNodeFactory.createNewSequence();
+        assertEquals(NodeEquality.IDENTICAL, rule.compare(sequence1, sequence1));
+
+        ISequence sequence2 = treeNodeFactory.createNewSequence();
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(sequence1, sequence2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(sequence2, sequence1));
+        
+        treeBuilder.addChild(sequence1, task1);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence1, sequence2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence2, sequence1));
+        
+        treeBuilder.addChild(sequence2, task1);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(sequence1, sequence2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(sequence2, sequence1));
+        
+        treeBuilder.addChild(sequence1, task2);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence1, sequence2));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence2, sequence1));
+        
+        treeBuilder.addChild(sequence2, task2);
+        
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(sequence1, sequence2));
+        assertEquals(NodeEquality.LEXICALLY_EQUAL, rule.compare(sequence2, sequence1));
+        
+        ISequence sequence3 = treeNodeFactory.createNewSequence();
+        treeBuilder.addChild(sequence3, task2);
+        treeBuilder.addChild(sequence3, task1);
+        
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence1, sequence3));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence3, sequence1));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence2, sequence3));
+        assertEquals(NodeEquality.UNEQUAL, rule.compare(sequence3, sequence2));
+
+        ISelection selection = treeNodeFactory.createNewSelection();
+        assertNull(rule.compare(sequence1, selection));
+        assertNull(rule.compare(selection, sequence1));
+        assertNull(rule.compare(sequence2, selection));
+        assertNull(rule.compare(selection, sequence2));
+        assertNull(rule.compare(sequence3, selection));
+        assertNull(rule.compare(selection, sequence3));
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/AbstractTemporalRelationshipTC.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/AbstractTemporalRelationshipTC.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/AbstractTemporalRelationshipTC.java	(revision 922)
@@ -0,0 +1,80 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.junit.Before;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.temporalrelation.TemporalRelationshipRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.testutils.Utilities;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 28.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class AbstractTemporalRelationshipTC {
+
+    /** */
+    private List<IEventTask> events;
+
+    /** */
+    private ITaskTreeBuilder taskTreeBuilder = new TaskTreeBuilder();
+
+    /** */
+    private ITaskTreeNodeFactory taskTreeNodeFactory = new TaskTreeNodeFactory();
+
+    /** */
+    private NodeEqualityRuleManager nodeEqualityRuleManager =
+        Utilities.getNodeEqualityRuleManagerForTests();
+
+    /**
+   *
+   */
+    @Before
+    public void setUp() {
+        new TextConsole(Level.FINEST);
+        events = new ArrayList<IEventTask>();
+    }
+
+    /**
+     *
+     */
+    protected void simulateEvent(IEventType eventType, IEventTarget eventTarget) {
+        events.add(taskTreeNodeFactory.createNewEventTask(eventType, eventTarget));
+    }
+
+    /**
+     *
+     * @return
+     */
+    protected ITaskTree getTaskTree() {
+        ISequence sequence = taskTreeNodeFactory.createNewSequence();
+
+        for (IEventTask task : events) {
+            taskTreeBuilder.addChild(sequence, task);
+        }
+
+        TemporalRelationshipRuleManager ruleManager =
+            new TemporalRelationshipRuleManager(nodeEqualityRuleManager);
+
+        ruleManager.init();
+        ruleManager.applyRules(sequence, taskTreeBuilder, taskTreeNodeFactory, true);
+
+        return taskTreeNodeFactory.createTaskTree(sequence);
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRuleTest.java	(revision 922)
@@ -0,0 +1,212 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.tasktrees.TaskTreeChecker;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class DefaultGuiElementSequenceDetectionRuleTest extends AbstractTemporalRelationshipTC {
+    
+    /**
+     *
+     */
+    @Test
+    public void testOneInteractionOnOneElement() {
+        simulateEvent(new DummyInteraction("bla", 1), new DummyGUIElement("elem1"));
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence {" +
+             "  Event bla {}" +
+             "}", getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testManyInteractionsOnOneElement() {
+        IEventTarget eventTarget = new DummyGUIElement("elem1");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget);
+        simulateEvent(new DummyInteraction("blu", 1), eventTarget);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence {" +
+             "  Event bla {}" +
+             "  Event bli {}" +
+             "  Event blo {}" +
+             "  Event blu {}" +
+             "  Event bla {}" +
+             "}", getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testOneInteractionOnManyElements() {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        IEventTarget eventTarget3 = new DummyGUIElement("elem3");
+        IEventTarget eventTarget4 = new DummyGUIElement("elem4");
+        IEventTarget eventTarget5 = new DummyGUIElement("elem5");
+        IEventTarget eventTarget6 = new DummyGUIElement("elem6");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget3);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget5);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget6);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Sequence sequence1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence2 {" +
+             "    Event bli {}" +
+             "  }" +
+             "  Sequence sequence3 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence4 {" +
+             "    Event bli {}" +
+             "  }" +
+             "  Sequence sequence5 {" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence6 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testManyInteractionsOnManyElementsWithoutHierarchy() {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        IEventTarget eventTarget3 = new DummyGUIElement("elem3");
+        IEventTarget eventTarget4 = new DummyGUIElement("elem4");
+        IEventTarget eventTarget5 = new DummyGUIElement("elem5");
+        IEventTarget eventTarget6 = new DummyGUIElement("elem6");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget3);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget5);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget6);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Sequence sequence1 {" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence2 {" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence3 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence4 {" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence5 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence6 {" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "}", getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testManyInteractionsOnManyElementsWithHierarchy() {
+        DummyGUIElement parentA = new DummyGUIElement("A");
+        DummyGUIElement parentB = new DummyGUIElement("B", parentA);
+        DummyGUIElement parentC = new DummyGUIElement("C", parentB);
+        DummyGUIElement parentF = new DummyGUIElement("F", parentB);
+        DummyGUIElement parentH = new DummyGUIElement("H", parentA);
+        DummyGUIElement parentI = new DummyGUIElement("I", parentH);
+        
+        DummyGUIElement eventTargetD = new DummyGUIElement("D", parentC);
+        DummyGUIElement eventTargetE = new DummyGUIElement("E", parentC);
+        DummyGUIElement eventTargetG = new DummyGUIElement("G", parentF);
+        DummyGUIElement eventTargetJ = new DummyGUIElement("J", parentI);
+        
+        simulateEvent(new DummyInteraction("1", 1), eventTargetD);
+        simulateEvent(new DummyInteraction("2", 1), eventTargetD);
+        simulateEvent(new DummyInteraction("3", 1), eventTargetE);
+        simulateEvent(new DummyInteraction("4", 1), eventTargetD);
+        simulateEvent(new DummyInteraction("5", 1), eventTargetG);
+        simulateEvent(new DummyInteraction("6", 1), eventTargetG);
+        simulateEvent(new DummyInteraction("7", 1), eventTargetD);
+        simulateEvent(new DummyInteraction("8", 1), eventTargetJ);
+        simulateEvent(new DummyInteraction("9", 1), eventTargetG);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Sequence sequence1 {" +
+             "    Sequence sequence2 {" +
+             "      Sequence sequence3 {" +
+             "        Event 1 {}" +
+             "        Event 2 {}" +
+             "      }" +
+             "      Sequence sequence4 {" +
+             "        Event 3 {}" +
+             "      }" +
+             "      Sequence sequence5 {" +
+             "        Event 4 {}" +
+             "      }" +
+             "    }" +
+             "    Sequence sequence6 {" +
+             "      Event 5 {}" +
+             "      Event 6 {}" +
+             "    }" +
+             "    Sequence sequence7 {" +
+             "      Event 7 {}" +
+             "    }" +
+             "  }" +
+             "  Sequence sequence8 {" +
+             "    Event 8 {}" +
+             "  }" +
+             "  Sequence sequence9 {" +
+             "    Event 9 {}" +
+             "  }" +
+             "}", getTaskTree());
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiEventSequenceDetectionRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiEventSequenceDetectionRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiEventSequenceDetectionRuleTest.java	(revision 922)
@@ -0,0 +1,153 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.tasktrees.TaskTreeChecker;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class DefaultGuiEventSequenceDetectionRuleTest extends AbstractTemporalRelationshipTC {
+    
+    /**
+     *
+     */
+    @Test
+    public void testOneInteractionOnOneElement() {
+        simulateEvent(new DummyInteraction("bla", 1), new DummyGUIElement("elem1"));
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence {" +
+             "  Event bla {}" +
+             "}", getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testManyInteractionsOnOneElement() {
+        IEventTarget eventTarget = new DummyGUIElement("elem1");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget);
+        simulateEvent(new DummyInteraction("blu", 1), eventTarget);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence {" +
+             "  Event bla {}" +
+             "  Event bli {}" +
+             "  Event blo {}" +
+             "  Event blu {}" +
+             "  Event bla {}" +
+             "}", getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testOneInteractionOnManyElements() {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        IEventTarget eventTarget3 = new DummyGUIElement("elem3");
+        IEventTarget eventTarget4 = new DummyGUIElement("elem4");
+        IEventTarget eventTarget5 = new DummyGUIElement("elem5");
+        IEventTarget eventTarget6 = new DummyGUIElement("elem6");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget3);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget5);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget6);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Sequence sequence1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence2 {" +
+             "    Event bli {}" +
+             "  }" +
+             "  Sequence sequence3 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence4 {" +
+             "    Event bli {}" +
+             "  }" +
+             "  Sequence sequence5 {" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence6 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", getTaskTree());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testManyInteractionsOnManyElements() {
+        IEventTarget eventTarget1 = new DummyGUIElement("elem1");
+        IEventTarget eventTarget2 = new DummyGUIElement("elem2");
+        IEventTarget eventTarget3 = new DummyGUIElement("elem3");
+        IEventTarget eventTarget4 = new DummyGUIElement("elem4");
+        IEventTarget eventTarget5 = new DummyGUIElement("elem5");
+        IEventTarget eventTarget6 = new DummyGUIElement("elem6");
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget1);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget2);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget3);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget4);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget5);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("bla", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("bli", 1), eventTarget6);
+        simulateEvent(new DummyInteraction("blo", 1), eventTarget6);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence0 {" +
+             "  Sequence sequence1 {" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence2 {" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence3 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence4 {" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "  Sequence sequence5 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Sequence sequence6 {" +
+             "    Event bli {}" +
+             "    Event bla {}" +
+             "    Event bli {}" +
+             "    Event blo {}" +
+             "  }" +
+             "}", getTaskTree());
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultIterationDetectionRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultIterationDetectionRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultIterationDetectionRuleTest.java	(revision 922)
@@ -0,0 +1,301 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.tasktrees.TaskTreeChecker;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 28.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DefaultIterationDetectionRuleTest extends AbstractTemporalRelationshipTC {
+
+    /**
+     *
+     */
+    @Test
+    public void testInteractionIterationDetection() throws Exception {
+        IEventTarget element1 = new DummyGUIElement("elem1");
+        IEventType event1 = new DummyInteraction("bla", 1);
+        simulateEvent(event1, element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Event bla {}" +
+             "}", getTaskTree());
+
+        simulateEvent(event1, element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", getTaskTree());
+
+        simulateEvent(event1, element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", getTaskTree());
+
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, element1);
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "}", getTaskTree());
+
+        // now test with preceding and trailing other events
+        IEventType event2 = new DummyInteraction("bli", 1);
+        IEventType event3 = new DummyInteraction("blup", 1);
+
+        simulateEvent(event2, element1);
+        simulateEvent(event3, element1);
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, element1);
+        }
+        simulateEvent(event3, element1);
+        simulateEvent(event2, element1);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event bli {}" +
+             "  Event blup {}" +
+             "  Iteration iteration2 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event blup {}" +
+             "  Event bli {}" +
+             "}", getTaskTree());
+
+        // now test with iterations of iterations
+
+        for (int i = 0; i < 10; i++) {
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event1, element1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event2, element1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event3, element1);
+            }
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event bli {}" +
+             "  Event blup {}" +
+             "  Iteration iteration2 {" +
+             "    Event bla {}" +
+             "  }" +
+             "  Event blup {}" +
+             "  Event bli {}" +
+             "  Iteration iteration3 {" +
+             "    Sequence sequence2 {" +
+             "      Iteration iteration4 {" +
+             "        Event bla {}" +
+             "      }" +
+             "      Iteration iteration5 {" +
+             "        Event bli {}" +
+             "      }" +
+             "      Iteration iteration6 {" +
+             "        Event blup {}" +
+             "      }" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testSequenceIterationDetection() throws Exception {
+        IEventTarget element1 = new DummyGUIElement("elem1");
+        IEventType event1 = new DummyInteraction("bla", 1);
+        IEventType event2 = new DummyInteraction("bli", 1);
+        IEventType event3 = new DummyInteraction("blup", 1);
+        simulateEvent(event1, element1);
+        simulateEvent(event2, element1);
+        simulateEvent(event3, element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Event bla {}" +
+             "  Event bli {}" +
+             "  Event blup {}" +
+             "}", getTaskTree());
+
+        simulateEvent(event1, element1);
+        simulateEvent(event2, element1);
+        simulateEvent(event3, element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+        simulateEvent(event1, element1);
+        simulateEvent(event2, element1);
+        simulateEvent(event3, element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, element1);
+            simulateEvent(event2, element1);
+            simulateEvent(event3, element1);
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+        // now test with preceding and trailing other events
+        IEventType event4 = new DummyInteraction("ble", 1);
+        IEventType event5 = new DummyInteraction("blo", 1);
+        IEventType event6 = new DummyInteraction("blu", 1);
+        simulateEvent(event4, element1);
+        simulateEvent(event5, element1);
+        simulateEvent(event6, element1);
+        for (int i = 0; i < 10; i++) {
+            simulateEvent(event1, element1);
+            simulateEvent(event2, element1);
+            simulateEvent(event3, element1);
+        }
+        simulateEvent(event6, element1);
+        simulateEvent(event5, element1);
+        simulateEvent(event4, element1);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event ble {}" +
+             "  Event blo {}" +
+             "  Event blu {}" +
+             "  Iteration iteration2 {" +
+             "    Sequence sequence3 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event blu {}" +
+             "  Event blo {}" +
+             "  Event ble {}" +
+             "}", getTaskTree());
+
+        // now test with iterations of iterations
+        for (int i = 0; i < 10; i++) {
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event1, element1);
+                simulateEvent(event2, element1);
+                simulateEvent(event3, element1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event2, element1);
+                simulateEvent(event1, element1);
+                simulateEvent(event3, element1);
+            }
+            for (int j = 0; j < 5; j++) {
+                simulateEvent(event1, element1);
+                simulateEvent(event2, element1);
+                simulateEvent(event3, element1);
+            }
+        }
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration iteration1 {" +
+             "    Sequence sequence2 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event ble {}" +
+             "  Event blo {}" +
+             "  Event blu {}" +
+             "  Iteration iteration2 {" +
+             "    Sequence sequence3 {" +
+             "      Event bla {}" +
+             "      Event bli {}" +
+             "      Event blup {}" +
+             "    }" +
+             "  }" +
+             "  Event blu {}" +
+             "  Event blo {}" +
+             "  Event ble {}" +
+             "  Iteration iteration3 {" +
+             "    Sequence sequence4 {" +
+             "      Iteration iteration4 {" +
+             "        Sequence sequence4 {" +
+             "          Event bla {}" +
+             "          Event bli {}" +
+             "          Event blup {}" +
+             "        }" +
+             "      }" +
+             "      Iteration iteration5 {" +
+             "        Sequence sequence5 {" +
+             "          Event bli {}" +
+             "          Event bla {}" +
+             "          Event blup {}" +
+             "        }" +
+             "      }" +
+             "      Iteration iteration6 {" +
+             "        Sequence sequence6 {" +
+             "          Event bla {}" +
+             "          Event bli {}" +
+             "          Event blup {}" +
+             "        }" +
+             "      }" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TrackBarSelectionDetectionRuleTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TrackBarSelectionDetectionRuleTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TrackBarSelectionDetectionRuleTest.java	(revision 922)
@@ -0,0 +1,161 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITrackBar;
+import de.ugoe.cs.autoquest.tasktrees.TaskTreeChecker;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 28.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TrackBarSelectionDetectionRuleTest extends AbstractTemporalRelationshipTC {
+
+    /**
+     *
+     */
+    @Test
+    public void testSimpleDetection() throws Exception {
+        IGUIElement element1 = new DummyTrackBar();
+        simulateEvent(new ValueSelection<Integer>(1), element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration interation1 {" +
+             "    Selection selection1 {" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+        simulateEvent(new ValueSelection<Integer>(2), element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration interation1 {" +
+             "    Selection selection1 {" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+        simulateEvent(new ValueSelection<Integer>(3), element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration interation1 {" +
+             "    Selection selection1 {" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+        simulateEvent(new ValueSelection<Integer>(2), element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration interation1 {" +
+             "    Selection selection1 {" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+        simulateEvent(new ValueSelection<Integer>(3), element1);
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration interation1 {" +
+             "    Selection selection1 {" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testComplexDetection() throws Exception {
+        IGUIElement element1 = new DummyTrackBar();
+        simulateEvent(new ValueSelection<Integer>(1), element1);
+        simulateEvent(new ValueSelection<Integer>(2), element1);
+        simulateEvent(new ValueSelection<Integer>(3), element1);
+        simulateEvent(new ValueSelection<Integer>(1), element1);
+        simulateEvent(new DummyInteraction("bla", 1), element1);
+        simulateEvent(new DummyInteraction("bla", 2), element1);
+        simulateEvent(new ValueSelection<Integer>(2), element1);
+        simulateEvent(new ValueSelection<Integer>(1), element1);
+        simulateEvent(new DummyInteraction("bla", 3), element1);
+        simulateEvent(new ValueSelection<Integer>(3), element1);
+        simulateEvent(new ValueSelection<Integer>(2), element1);
+        simulateEvent(new ValueSelection<Integer>(3), element1);
+        simulateEvent(new DummyInteraction("bla", 1), element1);
+        simulateEvent(new DummyInteraction("bla", 2), element1);
+        simulateEvent(new ValueSelection<Integer>(1), element1);
+        simulateEvent(new ValueSelection<Integer>(1), element1);
+
+        new TaskTreeChecker().assertTaskTree
+            ("Sequence sequence1 {" +
+             "  Iteration interation1 {" +
+             "    Selection selection1 {" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "  Event bla {}" +
+             "  Event bla {}" +
+             "  Iteration interation2 {" +
+             "    Selection selection2 {" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "  Event bla {}" +
+             "  Iteration interation3 {" +
+             "    Selection selection3 {" +
+             "      Event ValueSelection {}" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "  Event bla {}" +
+             "  Event bla {}" +
+             "  Iteration interation4 {" +
+             "    Selection selection4 {" +
+             "      Event ValueSelection {}" +
+             "    }" +
+             "  }" +
+             "}", getTaskTree());
+    }
+
+    /**
+     * TODO comment
+     * 
+     * @version $Revision: $ $Date: 28.04.2012$
+     * @author 2012, last modified by $Author: patrick$
+     */
+    public class DummyTrackBar extends DummyGUIElement implements ITrackBar {
+
+        /**  */
+        private static final long serialVersionUID = 1L;
+
+         /**
+          *
+          */
+         public DummyTrackBar() {
+            super("DummyTrackBar");
+        }
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/testutils/Utilities.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/testutils/Utilities.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/testutils/Utilities.java	(revision 922)
@@ -0,0 +1,30 @@
+package de.ugoe.cs.autoquest.tasktrees.testutils;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 02.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class Utilities {
+    
+    /** */
+    private static final NodeEqualityRuleManager NODE_EQUALITY_RULE_MANAGER =
+        new NodeEqualityRuleManager();
+
+    static {
+        NODE_EQUALITY_RULE_MANAGER.init();
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public static NodeEqualityRuleManager getNodeEqualityRuleManagerForTests() {
+        return NODE_EQUALITY_RULE_MANAGER;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeImplTest.java
===================================================================
--- trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeImplTest.java	(revision 922)
+++ trunk/autoquest-core-tasktrees-test/src/test/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeImplTest.java	(revision 922)
@@ -0,0 +1,466 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeInfo;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.NodeInfo;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 02.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TaskTreeImplTest {
+    
+    /** */
+    private static final int MAX_TREE_DEPTH = 15;
+
+    /** */
+    private ITaskTreeBuilder taskTreeBuilder = new TaskTreeBuilder();
+
+    /** */
+    private ITaskTreeNodeFactory taskTreeNodeFactory = new TaskTreeNodeFactory();
+
+    /**
+     * @throws Exception
+     * 
+     */
+    @Test
+    public void testRandomTrees() throws Exception {
+        int noOfTrees = 10;
+        int noOfMaxChildren = 8;
+        int maxDepth = MAX_TREE_DEPTH;
+
+        for (int i = 0; i < noOfTrees; i++) {
+            System.err.println("iteration " + (i + 1) + ":");
+            System.err.println("  creating tree");
+            Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos =
+                new HashMap<ITaskTreeNode, ITaskTreeNodeInfo>();
+            ITaskTreeNode rootNode = createTree(noOfMaxChildren, maxDepth, treeInfos);
+            System.err.println("  creating task tree");
+            ITaskTree taskTree = taskTreeNodeFactory.createTaskTree(rootNode);
+
+            System.err.println("  validating task tree");
+            assertEquals(rootNode, taskTree.getRoot());
+            assertMapsEqual(treeInfos, taskTree.getTaskMap());
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @param taskMap
+     */
+    private void assertMapsEqual(Map<ITaskTreeNode, ITaskTreeNodeInfo> map1,
+                                 Map<ITaskTreeNode, ITaskTreeNodeInfo> map2)
+    {
+        try {
+            if (map1 == null) {
+                assertNull(map2);
+                return;
+            }
+
+            assertEquals(map1.size(), map2.size());
+
+            for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : map1.entrySet()) {
+                ITaskTreeNodeInfo value2 = map2.get(entry.getKey());
+                assertNotNull(value2);
+                assertEquals(entry.getValue().getTask(), value2.getTask());
+                assertEquals(entry.getValue().getNoOfOccurencesInTree(),
+                             value2.getNoOfOccurencesInTree());
+            }
+        }
+        catch (AssertionError e) {
+            dumpMap(map1);
+            dumpMap(map2);
+            throw e;
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param map2
+     */
+    private void dumpMap(Map<ITaskTreeNode, ITaskTreeNodeInfo> map) {
+        System.err.println();
+
+        if (map == null) {
+            System.err.println("map is null");
+        }
+        else {
+            System.err.println("map:");
+            for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : map.entrySet()) {
+                System.err.print("  ");
+                System.err.print(entry.getKey());
+                for (int i = entry.getKey().toString().length(); i < 49; i++) {
+                    System.err.print(" ");
+                }
+                System.err.print(" : ");
+                System.err.println(entry.getValue());
+            }
+        }
+
+        System.err.println();
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param noOfMaxChildren
+     * @param maxDepth
+     * @param treeInfos
+     * @return
+     */
+    private ITaskTreeNode createTree(int                                   maxNoOfChildren,
+                                     int                                   maxDepth,
+                                     Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        /*
+         * for (int i = 0; i < (MAX_TREE_DEPTH + 1 - maxDepth); i++) { System.err.print("  "); }
+         */
+
+        ITaskTreeNode tree;
+
+        // integrating the maximum depth here assures, that either something between 0 and 8 will
+        // be the type, or if the max depth decreases near 0 only event tasks will be created
+        // to finish the tree creation
+        int type = (int) (Math.random() * (Math.min(8, maxDepth)));
+
+        switch (type)
+        {
+            case 0: {
+                // System.err.print("creating new event task ");
+                tree = createNewEventTask(treeInfos);
+                break;
+            }
+            case 1: {
+                // System.err.print("reusing event task ");
+                tree = reuseEventTask(treeInfos);
+                break;
+            }
+            case 2: {
+                // System.err.println("creating new sequence {");
+                tree = createNewSequence(maxNoOfChildren, maxDepth, treeInfos);
+                break;
+            }
+            case 3: {
+                // System.err.println("reusing sequence {");
+                tree = reuseSequence(maxNoOfChildren, maxDepth, treeInfos);
+                break;
+            }
+            case 4: {
+                // System.err.println("creating new selection {");
+                tree = createNewSelection(maxNoOfChildren, maxDepth, treeInfos);
+                break;
+            }
+            case 5: {
+                // System.err.println("reusing selection {");
+                tree = reuseSelection(maxNoOfChildren, maxDepth, treeInfos);
+                break;
+            }
+            case 6: {
+                // System.err.println("creating new iteration {");
+                tree = createNewIteration(maxNoOfChildren, maxDepth, treeInfos);
+                break;
+            }
+            case 7: {
+                // System.err.println("reusing iteration {");
+                tree = reuseIteration(maxNoOfChildren, maxDepth, treeInfos);
+                break;
+            }
+            default: {
+                // System.err.print("creating new event task per default ");
+                tree = createNewEventTask(treeInfos);
+            }
+        }
+
+        /*
+         * if (!(tree instanceof InteractionTask)) { for (int i = 0; i < (MAX_TREE_DEPTH + 1 -
+         * maxDepth); i++) { System.err.print("  "); }
+         * 
+         * System.err.print("} "); }
+         * 
+         * System.err.println(tree);
+         */
+
+        return tree;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private IEventTask createNewEventTask(Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        Thread.sleep(2);
+        long id = System.currentTimeMillis();
+        IEventTask task =
+            taskTreeNodeFactory.createNewEventTask(new DummyInteraction("interaction" + id, 1),
+                                                   new DummyGUIElement("elem" + id));
+
+        treeInfos.put(task, new NodeInfo(task));
+
+        return task;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private IEventTask reuseEventTask(Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        int noOfEventTasks = 0;
+
+        for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+            if (entry.getKey() instanceof IEventTask) {
+                noOfEventTasks++;
+            }
+        }
+
+        if (noOfEventTasks > 0) {
+            noOfEventTasks = (int) (Math.random() * noOfEventTasks);
+
+            for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+                if (entry.getKey() instanceof IEventTask) {
+                    if (--noOfEventTasks <= 0) {
+                        return (IEventTask) entry.getKey();
+                    }
+                }
+            }
+        }
+        else {
+            return createNewEventTask(treeInfos);
+        }
+
+        throw new RuntimeException("this is an implementation error");
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private ISequence createNewSequence(int                                   maxNoOfChildren,
+                                        int                                   maxDepth,
+                                        Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        ISequence sequence = taskTreeNodeFactory.createNewSequence();
+
+        int noOfChildren = (int) (Math.random() * maxNoOfChildren);
+
+        for (int i = 0; i < noOfChildren; i++) {
+            ITaskTreeNode child = createTree(maxNoOfChildren, maxDepth - 1, treeInfos);
+
+            // through first removing an existing parent it is assured, that a parent is recorded
+            // only once. This is needed, because parent may be reused in a tree as well, but we
+            // always
+            // iterate the whole tree
+            ((NodeInfo) treeInfos.get(child)).removeParent(sequence);
+            ((NodeInfo) treeInfos.get(child)).addParent(sequence);
+            taskTreeBuilder.addChild(sequence, child);
+        }
+
+        treeInfos.put(sequence, new NodeInfo(sequence));
+        return sequence;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private ISequence reuseSequence(int                                   maxNoOfChildren,
+                                    int                                   maxDepth,
+                                    Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        int noOfSequences = 0;
+
+        for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+            if (entry.getKey() instanceof ISequence) {
+                noOfSequences++;
+            }
+        }
+
+        if (noOfSequences > 0) {
+            noOfSequences = (int) (Math.random() * noOfSequences);
+
+            for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+                if (entry.getKey() instanceof ISequence) {
+                    if (--noOfSequences <= 0) {
+                        return (ISequence) entry.getKey();
+                    }
+                }
+            }
+        }
+        else {
+            return createNewSequence(maxNoOfChildren, maxDepth, treeInfos);
+        }
+
+        throw new RuntimeException("this is an implementation error");
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private ISelection createNewSelection(int                                   maxNoOfChildren,
+                                          int                                   maxDepth,
+                                          Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        ISelection selection = taskTreeNodeFactory.createNewSelection();
+
+        int noOfChildren = (int) (Math.random() * maxNoOfChildren);
+
+        for (int i = 0; i < noOfChildren; i++) {
+            ITaskTreeNode child = createTree(maxNoOfChildren, maxDepth - 1, treeInfos);
+
+            // through first removing an existing parent it is assured, that a parent is recorded
+            // only once. This is needed, because parent may be reused in a tree as well, but we
+            // always
+            // iterate the whole tree
+            ((NodeInfo) treeInfos.get(child)).removeParent(selection);
+            ((NodeInfo) treeInfos.get(child)).addParent(selection);
+            taskTreeBuilder.addChild(selection, child);
+        }
+
+        treeInfos.put(selection, new NodeInfo(selection));
+        return selection;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private ISelection reuseSelection(int                                   maxNoOfChildren,
+                                      int                                   maxDepth,
+                                      Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        int noOfSelections = 0;
+
+        for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+            if (entry.getKey() instanceof ISelection) {
+                noOfSelections++;
+            }
+        }
+
+        if (noOfSelections > 0) {
+            noOfSelections = (int) (Math.random() * noOfSelections);
+
+            for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+                if (entry.getKey() instanceof ISelection) {
+                    if (--noOfSelections <= 0) {
+                        return (ISelection) entry.getKey();
+                    }
+                }
+            }
+        }
+        else {
+            return createNewSelection(maxNoOfChildren, maxDepth, treeInfos);
+        }
+
+        throw new RuntimeException("this is an implementation error");
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private IIteration createNewIteration(int                                   maxNoOfChildren,
+                                          int                                   maxDepth,
+                                          Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        IIteration iteration = taskTreeNodeFactory.createNewIteration();
+
+        ITaskTreeNode child = createTree(maxNoOfChildren, maxDepth - 1, treeInfos);
+
+        // through first removing an existing parent it is assured, that a parent is recorded
+        // only once. This is needed, because parent may be reused in a tree as well, but we always
+        // iterate the whole tree
+        ((NodeInfo) treeInfos.get(child)).removeParent(iteration);
+        ((NodeInfo) treeInfos.get(child)).addParent(iteration);
+        taskTreeBuilder.setChild(iteration, child);
+
+        treeInfos.put(iteration, new NodeInfo(iteration));
+        return iteration;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeInfos
+     * @return
+     */
+    private IIteration reuseIteration(int                                   maxNoOfChildren,
+                                      int                                   maxDepth,
+                                      Map<ITaskTreeNode, ITaskTreeNodeInfo> treeInfos)
+        throws Exception
+    {
+        int noOfIterations = 0;
+
+        for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+            if (entry.getKey() instanceof IIteration) {
+                noOfIterations++;
+            }
+        }
+
+        if (noOfIterations > 0) {
+            noOfIterations = (int) (Math.random() * noOfIterations);
+
+            for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : treeInfos.entrySet()) {
+                if (entry.getKey() instanceof IIteration) {
+                    if (--noOfIterations <= 0) {
+                        return (IIteration) entry.getKey();
+                    }
+                }
+            }
+        }
+        else {
+            return createNewIteration(maxNoOfChildren, maxDepth, treeInfos);
+        }
+
+        throw new RuntimeException("this is an implementation error");
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-core-tasktrees/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-core-tasktrees/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/manager/ComponentManager.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/manager/ComponentManager.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/manager/ComponentManager.java	(revision 922)
@@ -0,0 +1,107 @@
+package de.ugoe.cs.autoquest.tasktrees.manager;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.temporalrelation.TemporalRelationshipRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 12.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class ComponentManager {
+    
+    /** */
+    private static ComponentManager instance;
+
+    /** */
+    private TemporalRelationshipRuleManager temporalRelationshipRuleManager;
+
+    /** */
+    private NodeEqualityRuleManager nodeEqualityRuleManager;
+
+    /** */
+    private ITaskTreeBuilder taskTreeBuilder;
+
+    /** */
+    private ITaskTreeNodeFactory taskTreeNodeFactory;
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public static TemporalRelationshipRuleManager getTemporalRelationshipRuleManager() {
+        return getInstance().temporalRelationshipRuleManager;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public static NodeEqualityRuleManager getNodeEqualityRuleManager() {
+        return getInstance().nodeEqualityRuleManager;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public static ITaskTreeBuilder getDefaultTaskTreeBuilder() {
+        return getInstance().taskTreeBuilder;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public static ITaskTreeNodeFactory getDefaultTaskTreeNodeFactory() {
+        return getInstance().taskTreeNodeFactory;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public static synchronized void clearInstance() {
+        instance = null;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    private static synchronized ComponentManager getInstance() {
+        if (instance == null) {
+            instance = new ComponentManager();
+            instance.init();
+        }
+        return instance;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    private void init() {
+        nodeEqualityRuleManager = new NodeEqualityRuleManager();
+        nodeEqualityRuleManager.init();
+
+        temporalRelationshipRuleManager =
+            new TemporalRelationshipRuleManager(nodeEqualityRuleManager);
+        temporalRelationshipRuleManager.init();
+
+        taskTreeBuilder = new TaskTreeBuilder();
+        taskTreeNodeFactory = new TaskTreeNodeFactory();
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/manager/TaskTreeManager.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/manager/TaskTreeManager.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/manager/TaskTreeManager.java	(revision 922)
@@ -0,0 +1,119 @@
+package de.ugoe.cs.autoquest.tasktrees.manager;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class TaskTreeManager {
+    
+    /** */
+    private static final int MAX_EVENTS_TILL_RULE_APPLICATION = 100;
+
+    /** */
+    private ITaskTreeBuilder taskTreeBuilder = ComponentManager.getDefaultTaskTreeBuilder();
+
+    /** */
+    private ITaskTreeNodeFactory taskTreeNodeFactory =
+        ComponentManager.getDefaultTaskTreeNodeFactory();
+
+    /** */
+    private int eventsTillRuleApplication = MAX_EVENTS_TILL_RULE_APPLICATION;
+
+    /** */
+    private ISequence rootSequence;
+
+    /**
+     * TODO: comment
+     * 
+     */
+    public TaskTreeManager() {
+        rootSequence = taskTreeNodeFactory.createNewSequence();
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param sequences
+     * @return
+     */
+    public synchronized ITaskTree createTaskTree(Collection<List<Event>> sequences) {
+        
+        for (List<Event> sequence : sequences) {
+            for (Event event : sequence) {
+                handleNewEvent(event);
+            }
+        }
+        
+        return getTaskTree();
+    }
+
+    /**
+     *
+     */
+    public void handleNewEvent(Event event) {
+        handleEventTask(taskTreeNodeFactory.createNewEventTask(event.getType(), event.getTarget()));
+    }
+
+    /**
+     *
+     */
+    public synchronized ITaskTree getTaskTree() {
+        Console.traceln(Level.INFO, "applying temporal relationship generation rules");
+
+        ISequence currentRootSequence = rootSequence.clone();
+        ComponentManager.getTemporalRelationshipRuleManager().applyRules
+          (currentRootSequence, taskTreeBuilder, taskTreeNodeFactory, true);
+
+        return taskTreeNodeFactory.createTaskTree(currentRootSequence);
+    }
+
+    /**
+     * adds the task to the current or the new sequence. The decision depends on the type of task.
+     * If the task finishes the current sequence, the sequence is marked as finished If the task
+     * does not start a new sequence, it is added to the current sequence, before it is marked s
+     * finished. Otherwise it is added to a new sequence.
+     */
+    private synchronized void handleEventTask(IEventTask eventTask) {
+        if (!(eventTask.getEventType() instanceof KeyboardFocusChange)) {
+            Console.traceln(Level.INFO, "handling interaction event task \"" + eventTask + "\"");
+            addTaskToSequence(eventTask);
+        }
+    }
+
+    /**
+     *
+     */
+    private void addTaskToSequence(ITaskTreeNode task)
+    {
+        taskTreeBuilder.addChild(rootSequence, task);
+
+        if (--eventsTillRuleApplication == 0) {
+            eventsTillRuleApplication = MAX_EVENTS_TILL_RULE_APPLICATION;
+
+            Console.traceln(Level.INFO, "applying temporal relationship generation rules");
+            ComponentManager.getTemporalRelationshipRuleManager().applyRules(rootSequence,
+                                                                             taskTreeBuilder,
+                                                                             taskTreeNodeFactory,
+                                                                             false);
+        }
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/EventTaskComparisonRule.java	(revision 922)
@@ -0,0 +1,44 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * This rule identifies two task tree nodes as lexically equal, if they are both event tasks and
+ * if their respective event types and targets equal. 
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+public class EventTaskComparisonRule implements NodeComparisonRule {
+    
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if ((!(node1 instanceof IEventTask)) || (!(node2 instanceof IEventTask))) {
+            return null;
+        }
+
+        if (node1 == node2) {
+            return NodeEquality.IDENTICAL;
+        }
+
+        IEventTask task1 = (IEventTask) node1;
+        IEventTask task2 = (IEventTask) node2;
+        
+        if (task1.getEventType().equals(task2.getEventType()) &&
+            task1.getEventTarget().equals(task2.getEventTarget()))
+        {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/GUIEventTaskComparisonRule.java	(revision 922)
@@ -0,0 +1,147 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * This rule compares GUI event tasks (i.e. it is more concrete, than the
+ * {@link EventTaskComparisonRule}). Two GUI event tasks are only equal if their event type and
+ * target are equal. The returned equality is even more fine-grained for events whose type is
+ * {@link TextInput} and {@link ValueSelection}. For text inputs, lexical equality is returned if
+ * the same text is entered using the same key interactions. Syntactical equality is returned if
+ * the same text is entered using different key interactions. Semantical equality is returned if
+ * different text is entered, but into the same event target. Value selections are syntactically
+ * equal, if the same value is selected. Otherwise they are semantically equal.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+public class GUIEventTaskComparisonRule implements NodeComparisonRule {
+    
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if ((!(node1 instanceof IEventTask)) || (!(node2 instanceof IEventTask))) {
+            return null;
+        }
+        
+        IEventTask task1 = (IEventTask) node1;
+        IEventTask task2 = (IEventTask) node2;
+        
+        if ((!(task1.getEventType() instanceof IInteraction)) ||
+            (!(task2.getEventType() instanceof IInteraction)))
+        {
+            return null;
+        }
+
+        if (node1 == node2) {
+            return NodeEquality.IDENTICAL;
+        }
+
+        if (!task1.getEventTarget().equals(task2.getEventTarget())) {
+            return NodeEquality.UNEQUAL;
+        }
+        
+        IInteraction interaction1 = (IInteraction) task1.getEventType();
+        IInteraction interaction2 = (IInteraction) task2.getEventType();
+        
+        return compareInteractions(interaction1, interaction2);
+    }
+
+    /**
+     * <p>
+     * compares two interactions. The method delegates two
+     * {@link #compareTextInputs(TextInput, TextInput)} and
+     * {@link #compareValueSelections(ValueSelection, ValueSelection)} for text inputs and value
+     * selections. Otherwise it uses the equal method of the interactions for comparison. In this
+     * case, if the interactions equal method returns true, this method returns lexical equality.
+     * </p>
+     *
+     * @param interaction1 the first interaction to compare
+     * @param interaction2 the second interaction to compare
+     * 
+     * @return as described
+     */
+    private NodeEquality compareInteractions(IInteraction interaction1, IInteraction interaction2) {
+        if (interaction1 == interaction2) {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+        else if ((interaction1 instanceof TextInput) && (interaction2 instanceof TextInput)) {
+            return compareTextInputs((TextInput) interaction1, (TextInput) interaction2);
+        }
+        else if ((interaction1 instanceof ValueSelection) &&
+                 (interaction2 instanceof ValueSelection))
+        {
+            return compareValueSelections
+                ((ValueSelection<?>) interaction1, (ValueSelection<?>) interaction2);
+        }
+        else if (interaction1.equals(interaction2)) {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+    }
+
+    /**
+     * <p>
+     * compares two text inputs. If both text inputs have the same entered text and text input
+     * events, they are lexically equal. If they only have the same entered text, they are
+     * syntactically equal. If they are only both text inputs, they are semantically equal.
+     * (the equality of the event targets is checked beforehand).
+     * </p>
+     *
+     * @param interaction1 the first text input to compare
+     * @param interaction2 the second text input to compare
+     * 
+     * @return as described
+     */
+    private NodeEquality compareTextInputs(TextInput interaction1, TextInput interaction2) {
+        if (interaction1.getEnteredText().equals(interaction2.getEnteredText())) {
+            if (interaction1.getTextInputEvents().equals(interaction2.getTextInputEvents())) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+            else {
+                return NodeEquality.SYNTACTICALLY_EQUAL;
+            }
+        }
+        else {
+            return NodeEquality.SEMANTICALLY_EQUAL;
+        }
+    }
+
+    /**
+     * <p>
+     * compares two value selections. If both value selections have the same selected value, they
+     * are syntactically equal, otherwise they are semantically equal.
+     * (the equality of the event targets is checked beforehand).
+     * </p>
+     *
+     * @param interaction1 the first value selection to compare
+     * @param interaction2 the second value selection to compare
+     * 
+     * @return as described
+     */
+    private NodeEquality compareValueSelections(ValueSelection<?> interaction1,
+                                                ValueSelection<?> interaction2)
+    {
+        Object value1 = interaction1.getSelectedValue();
+        Object value2 = interaction2.getSelectedValue();
+        
+        if ((value1 == value2) || ((value1 != null) && (value1.equals(value2)))) {
+            return NodeEquality.SYNTACTICALLY_EQUAL;
+        }
+        else {
+            return NodeEquality.SEMANTICALLY_EQUAL;
+        }
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/IterationComparisonRule.java	(revision 922)
@@ -0,0 +1,191 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * This class is capable of comparing Iterations. Iterations equal at distinct levels
+ * in distinct situations. The following table shows the results of the comparison for the
+ * specific situations (the parameters are commutative). In any other situation, the comparison
+ * returns <code>NodeEquality.UNEQUAL</code>:
+ * </p>
+ * 
+ * <table border="1">
+ *   <tr>
+ *     <th>iteration 1</th>
+ *     <th>iteration 2</th>
+ *     <th>comparison result</th>
+ *   </tr>
+ *   <tr>
+ *     <td>any iteration</td>
+ *     <td>any iteration with a child that is lexically equal to the child of iteration 1</td>
+ *     <td><code>NodeEquality.LEXICALLY_EQUAL</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td>any iteration</td>
+ *     <td>any iteration with a child that is syntactically equal to the child of iteration 1</td>
+ *     <td><code>NodeEquality.SYNTACTICALLY_EQUAL</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td>any iteration</td>
+ *     <td>any iteration with a child that is semantically equal to the child of iteration 1</td>
+ *     <td><code>NodeEquality.SEMANTICALLY_EQUAL</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td>an iteration with a selection of syntactically equal children</td>
+ *     <td>an iteration with a child that is syntactically equal to the children of the child
+ *     selection of iteration 1</td>
+ *     <td><code>NodeEquality.SYNTACTICALLY_EQUAL</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td>an iteration with a selection of syntactically equal children</td>
+ *     <td>an iteration with a selection of syntactically equal children that are all syntactically
+ *     equal to the selection of children of iteration 1</td>
+ *     <td><code>NodeEquality.SYNTACTICALLY_EQUAL</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td>an iteration with a selection of semantically equal children</td>
+ *     <td>an iteration with a child that is semantically equal to the children of the child
+ *     selection of iteration 1</td>
+ *     <td><code>NodeEquality.SEMANTICALLY_EQUAL</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td>an iteration with a selection of semantically equal children</td>
+ *     <td>an iteration with a selection of semantically equal children that are all semantically
+ *     equal to the selection of children of iteration 1</td>
+ *     <td><code>NodeEquality.SEMANTICALLY_EQUAL</code></td>
+ *   </tr>
+ * </table>
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class IterationComparisonRule implements NodeComparisonRule {
+    
+    /** the rule manager for internally comparing task tree nodes */
+    private NodeEqualityRuleManager mRuleManager;
+
+    /**
+     * <p>
+     * simple constructor to provide the rule with the node equality rule manager to be able
+     * to perform comparisons of the children of provided task tree nodes
+     * </p>
+     * 
+     * @param ruleManager the rule manager for comparing task tree nodes
+     */
+    IterationComparisonRule(NodeEqualityRuleManager ruleManager) {
+        super();
+        mRuleManager = ruleManager;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if ((!(node1 instanceof IIteration)) || (!(node2 instanceof IIteration))) {
+            return null;
+        }
+
+        if (node1 == node2) {
+            return NodeEquality.IDENTICAL;
+        }
+
+        // if both iterations do not have children, they are equal although this doesn't make sense
+        if ((node1.getChildren().size() == 0) && (node2.getChildren().size() == 0)) {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+        else if ((node1.getChildren().size() == 0) || (node2.getChildren().size() == 0)) {
+            return NodeEquality.UNEQUAL;
+        }
+
+        ITaskTreeNode child1 = node1.getChildren().get(0);
+        ITaskTreeNode child2 = node2.getChildren().get(0);
+
+        // iterations may have 3 different structures.
+        // 1. they have one child, which is the iterated one
+        // 2. they have a sequence of children, which is iterated
+        // 3. they have a selection of different iterated variants (usually the variants are
+        // semantically equal)
+        //
+        // the permutations of the three variants in combination must be checked
+
+        // check if both nodes are the same variants of iterations and if their children are equal.
+        // This condition matches, if both iterations are the same variants of iteration. I.e. three
+        // combinations of the permutation are handled herewith.
+        NodeEquality nodeEquality = mRuleManager.applyRules(child1, child2);
+
+        if (nodeEquality.isAtLeast(NodeEquality.SEMANTICALLY_EQUAL)) {
+            // prevent, that identical is returned, because the iterations itself are not identical
+            // although the iterated tasks are
+            if (nodeEquality == NodeEquality.IDENTICAL) {
+                return NodeEquality.LEXICALLY_EQUAL;
+            }
+            else {
+                return nodeEquality;
+            }
+        }
+
+        // compare one iteration with a single node as a child and another one with a selection of
+        // semantically equal nodes
+        return selectionChildrenSemanticallyEqualNode(child1, child2);
+        
+        // all other combinations (i.e. sequence with single child and sequence with selection)
+        // can not match
+    }
+
+    /**
+     * <p>
+     * compares two task tree nodes. One of them must be a selection, the other one can be any task
+     * tree node. The method returns a node equality that is not <code>NodeEquality.UNEQUAL</code>
+     * if the other node is at least semantically equal to the children of the selection. It
+     * returns more concrete equalities, if the equality between the other node and the children
+     * of the selection is more concrete.
+     * </p> 
+     * 
+     * @param taskTreeNode  the first task tree node to compare
+     * @param taskTreeNode2 the second task tree node to compare
+     * 
+     * @return as described
+     */
+    private NodeEquality selectionChildrenSemanticallyEqualNode(ITaskTreeNode taskTreeNode,
+                                                                ITaskTreeNode taskTreeNode2)
+    {
+        ISelection selection = null;
+        ITaskTreeNode node = null;
+        if (taskTreeNode instanceof ISelection) {
+            selection = (ISelection) taskTreeNode;
+            node = taskTreeNode2;
+        }
+        else if (taskTreeNode2 instanceof ISelection) {
+            selection = (ISelection) taskTreeNode2;
+            node = taskTreeNode;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+
+        // Iterations, where one has a selection and the other one not can at most be syntactically
+        // equal but not identical
+        NodeEquality commonDenominatorForAllComparisons = NodeEquality.SYNTACTICALLY_EQUAL;
+
+        for (ITaskTreeNode child : selection.getChildren()) {
+            NodeEquality nodeEquality = mRuleManager.applyRules(node, child);
+
+            if ((nodeEquality == null) || (nodeEquality == NodeEquality.UNEQUAL))
+            {
+                return NodeEquality.UNEQUAL;
+            }
+            
+            commonDenominatorForAllComparisons =
+                commonDenominatorForAllComparisons.getCommonDenominator(nodeEquality);
+        }
+
+        return commonDenominatorForAllComparisons;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndIterationComparisonRule.java	(revision 922)
@@ -0,0 +1,86 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * This class is capable of comparing any task tree node which is not an iteration with an
+ * iteration. This is needed, because iterations may iterate exactly that node. In this
+ * case, the iteration would be equal to that node if it was executed exactly once. The rule
+ * returns lexically equal, it the child of the iteration is lexically equal to the node
+ * or if the child of the iteration is a selection and this selections contains a lexically equal
+ * node. The same applies for syntactical and semantical equality.
+ * </p>
+
+ * @author Patrick Harms
+ */
+public class NodeAndIterationComparisonRule implements NodeComparisonRule {
+    
+    /** the rule manager for internally comparing task tree nodes */
+    private NodeEqualityRuleManager mRuleManager;
+
+    /**
+     * <p>
+     * simple constructor to provide the rule with the node equality rule manager to be able
+     * to perform comparisons of the children of provided task tree nodes
+     * </p>
+     * 
+     * @param ruleManager the rule manager for comparing task tree nodes
+     */
+    NodeAndIterationComparisonRule(NodeEqualityRuleManager ruleManager) {
+        super();
+        mRuleManager = ruleManager;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        IIteration iteration = null;
+        ITaskTreeNode node = null;
+        
+        if (node1 instanceof IIteration) {
+            if (node2 instanceof IIteration) {
+                // the rule is not responsible for two iterations
+                return null;
+            }
+            
+            iteration = (IIteration) node1;
+            node = node2;
+        }
+        else if (node2 instanceof IIteration) {
+            if (node1 instanceof IIteration) {
+                // the rule is not responsible for two iterations
+                return null;
+            }
+            
+            iteration = (IIteration) node2;
+            node = node1;
+        }
+        else {
+            return null;
+        }
+
+        // now, that we found the iteration and the node, lets compare the child of the iteration
+        // with the node.
+        if (iteration.getChildren().size() < 1) {
+            return null;
+        }
+
+        NodeEquality nodeEquality = mRuleManager.applyRules(iteration.getChildren().get(0), node);
+
+        // although the subtask may be identical to the node, we can not return identical, as
+        // the iteration is not identical to the node, but at most lexically equal
+        if (nodeEquality == NodeEquality.IDENTICAL) {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+        else {
+            return nodeEquality;
+        }
+
+    }
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeAndSelectionComparisonRule.java	(revision 922)
@@ -0,0 +1,99 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * This class is capable of comparing any task tree node which is not a selection with a
+ * selection. This is needed, because selections may contain exactly that node. Therefore, if
+ * this node is selected out of a selection the selection is equal to the node itself. 
+ * The rule returns lexically equal, it the selection contains a lexically equal node. The same
+ * applies for syntactical and semantical equality.
+ * </p>
+
+ * @author Patrick Harms
+ */
+public class NodeAndSelectionComparisonRule implements NodeComparisonRule {
+    
+    /** the rule manager for internally comparing task tree nodes */
+    private NodeEqualityRuleManager mRuleManager;
+
+    /**
+     * <p>
+     * simple constructor to provide the rule with the node equality rule manager to be able
+     * to perform comparisons of the children of provided task tree nodes
+     * </p>
+     * 
+     * @param ruleManager the rule manager for comparing task tree nodes
+     */
+    NodeAndSelectionComparisonRule(NodeEqualityRuleManager ruleManager) {
+        super();
+        mRuleManager = ruleManager;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        ISelection selection = null;
+        ITaskTreeNode node = null;
+        
+        if (node1 instanceof ISelection) {
+            if (node2 instanceof ISelection) {
+                // the rule is not responsible for two iterations
+                return null;
+            }
+            
+            selection = (ISelection) node1;
+            node = node2;
+        }
+        else if (node2 instanceof ISelection) {
+            if (node1 instanceof ISelection) {
+                // the rule is not responsible for two iterations
+                return null;
+            }
+            
+            selection = (ISelection) node2;
+            node = node1;
+        }
+        else {
+            return null;
+        }
+
+        // now, that we found the iteration and the node, lets compare the child of the iteration
+        // with the node.
+        if (selection.getChildren().size() < 1) {
+            return null;
+        }
+
+        NodeEquality mostConcreteNodeEquality = null;
+        
+        for (ITaskTreeNode child : selection.getChildren()) {
+            NodeEquality nodeEquality = mRuleManager.applyRules(child, node);
+            
+            if (nodeEquality != NodeEquality.UNEQUAL) {
+                if (mostConcreteNodeEquality == null) {
+                    mostConcreteNodeEquality = nodeEquality;
+                }
+                else {
+                    mostConcreteNodeEquality =
+                        mostConcreteNodeEquality.getCommonDenominator(nodeEquality);
+                }
+            }
+        }
+        
+        // although the subtask may be identical to the node, we can not return identical, as
+        // the selection is not identical to the node, but at most lexically equal
+        if (mostConcreteNodeEquality == NodeEquality.IDENTICAL) {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+        else {
+            return mostConcreteNodeEquality;
+        }
+
+    }
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeComparisonRule.java	(revision 922)
@@ -0,0 +1,30 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * A node comparison rule is used by the {@link NodeEqualityRuleManager} to compare task tree
+ * nodes with each other. It provides one method to be called for a comparison.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface NodeComparisonRule {
+
+    /**
+     * <p>
+     * compares two nodes with each other. The result of the method is either a node equality or
+     * null. If it is null, it means, that the rule is not able to correctly compare the two given
+     * nodes
+     * </p>
+     * 
+     * @param node1 the first task tree node to compare
+     * @param node2 the second task tree node to compare
+     * 
+     * @return as described
+     */
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2);
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEquality.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEquality.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEquality.java	(revision 922)
@@ -0,0 +1,113 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+/**
+ * <p>
+ * A node equality denotes, how equal two task tree nodes are. There are different equality levels
+ * which are similar to the usual design levels of GUI design. These levels are
+ * <ul>
+ *   <li>conceptual design: defines the concepts to be edited using a GUI</li>
+ *   <li>semantical design: defines the possible functions for editing the concepts</li>
+ *   <li>syntactical design: defines, which steps are needed to execute the functions</li>
+ *   <li>lexical design: defines on the key stroke level, how the steps for executing a function
+ *       can be performed</li>
+ * </ul>
+ * It is not possible to compare two task nodes conceptually. But the other design levels can be
+ * identified and compared.
+ * </p>
+ * <p>
+ * Nodes can be identical. This is the case if in the java virtual machine, their comparison
+ * using the <code>==</code> operator or the equals method return true.
+ * </p>
+ * <p>
+ * Nodes are lexically equal, if they represent the same events on a key stroke level to be
+ * carried out to execute the task. Identical nodes are also syntactically equal.
+ * </p>
+ * <p>
+ * Nodes are syntactically equal, if they differ in their events on key stroke level, but the
+ * syntactical result is the same. For example, entering the text "hello" into a text field can
+ * be done by entering the letters in their correct order, but also by copying the text into the
+ * text field. The syntactical result is the same: The text hello was entered. But the tasks
+ * lexically differ because the events on key stroke level are different. On the other hand,
+ * lexically equal nodes are also syntactically equal.  
+ * </p>
+ * <p>
+ * Task tree nodes are semantically equal, if they execute the same function for editing the
+ * concepts. An example are a click on a button and a short cut, both executing the same function.
+ * These task tree nodes are syntactically and, therefore, also lexically different, but
+ * semantically equal. Syntactically equal task tree nodes are always also semantically equal.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public enum NodeEquality {
+    IDENTICAL,
+    LEXICALLY_EQUAL,
+    SYNTACTICALLY_EQUAL,
+    SEMANTICALLY_EQUAL,
+    UNEQUAL;
+
+    /**
+     * <p>
+     * Checks for the current node equality, if it is at least identical to the
+     * provided one or even more concrete. As an example, the node equality identical also
+     * indicates, that the nodes are e.g. lexically, syntactically and semantically equal.
+     * Therefore, the method called on <code>IDENTICAL</code> with <code>SEMANTICALLY_EQUAL</code>
+     * as parameter will return true. If this method is called on <code>SYNTACTICALLY_EQUAL</code>
+     * with the parameter <code>IDENTICAL</code> instead, it returns false;
+     * </p>
+     *
+     * @param nodeEquality the node equality to compare with.
+     * 
+     * @return as described
+     */
+    public boolean isAtLeast(NodeEquality nodeEquality)
+    {
+        switch (nodeEquality) {
+            case IDENTICAL:
+                return
+                    (this == IDENTICAL);
+            case LEXICALLY_EQUAL:
+                return
+                    (this == IDENTICAL) ||
+                    (this == LEXICALLY_EQUAL);
+            case SYNTACTICALLY_EQUAL:
+                return
+                    (this == IDENTICAL) ||
+                    (this == LEXICALLY_EQUAL) ||
+                    (this == SYNTACTICALLY_EQUAL);
+            case SEMANTICALLY_EQUAL:
+                return
+                    (this == IDENTICAL) ||
+                    (this == LEXICALLY_EQUAL) ||
+                    (this == SYNTACTICALLY_EQUAL) ||
+                    (this == SEMANTICALLY_EQUAL);
+            case UNEQUAL:
+                return
+                    (this == UNEQUAL);
+            default :
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * returns the common denominator of this node equality and the provided one. I.e. if one
+     * equality is e.g. syntactical and the other one only semantical, then semantical is returned.
+     * </p>
+     *
+     * @param equality the equality, to compare this with
+     * @return
+     */
+    public NodeEquality getCommonDenominator(NodeEquality otherEquality) {
+        if (this.isAtLeast(otherEquality)) {
+            return otherEquality;
+        }
+        else if (otherEquality.isAtLeast(this)) {
+            return this;
+        }
+        else {
+            return NodeEquality.UNEQUAL;
+        }
+    }
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityRuleManager.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityRuleManager.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeEqualityRuleManager.java	(revision 922)
@@ -0,0 +1,84 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * The node equality rule manager is capable of comparing task tree nodes based on its internal list
+ * of comparison rules. The current list of rules contains the {@link NodeIdentityRule}, the
+ * {@link IterationComparisonRule}, the {@link SequenceComparisonRule}, and
+ * {@link SelectionComparisonRule}. These rules are asked for comparing the two provided task tree
+ * nodes in the mentioned order. If a rule returns a node equality other than null, this equality is
+ * returned. Otherwise the next rule is asked.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class NodeEqualityRuleManager {
+
+    /** */
+    private List<NodeComparisonRule> mRuleIndex = null;
+
+    /**
+     * <p>
+     * initializes the node equality rule manager by filling the internal list of comparison rules.
+     * This method must be called before any other method is called on the rule manager.
+     * </p>
+     */
+    public void init() {
+        mRuleIndex = new ArrayList<NodeComparisonRule>();
+        mRuleIndex.add(new NodeIdentityRule());
+        mRuleIndex.add(new GUIEventTaskComparisonRule());
+        mRuleIndex.add(new EventTaskComparisonRule());
+        mRuleIndex.add(new IterationComparisonRule(this));
+        mRuleIndex.add(new SequenceComparisonRule(this));
+        mRuleIndex.add(new SelectionComparisonRule(this));
+        mRuleIndex.add(new NodeAndIterationComparisonRule(this));
+        mRuleIndex.add(new NodeAndSelectionComparisonRule(this));
+    }
+
+    /**
+     * <p>
+     * this method performs a comparison of the two provided task tree nodes. It iterates its
+     * internal comparison rules. If the first rule returns a node equality other than null,
+     * this equality is returned. Otherwise the next rule is tried. If no rule returns an equality
+     * <code>NodeEquality.UNEQUAL</code> is returned.
+     * </p>
+     * 
+     * @param node1 the first task tree node to be compared
+     * @param node2 the second task tree node to be compared
+     * 
+     * @return as described
+     * 
+     * @throws IllegalStateException in the case, the {@link #init()} method was not called on the
+     *                               manager before a call to this method.
+     */
+    public NodeEquality applyRules(ITaskTreeNode node1, ITaskTreeNode node2)
+        throws IllegalStateException
+    {
+        if (mRuleIndex == null) {
+            throw new IllegalStateException("not initialized");
+        }
+        
+        // LOG.info("checking for equality of " + node1 + " and " + node2);
+        NodeEquality nodeEquality = null;
+
+        for (NodeComparisonRule rule : mRuleIndex) {
+            nodeEquality = rule.compare(node1, node2);
+
+            if (nodeEquality != null) {
+                // LOG.warning("used rule " + rule + " for equality check");
+                return nodeEquality;
+            }
+        }
+
+        // LOG.warning("no rule could be applied --> handling nodes as unequal");
+
+        return NodeEquality.UNEQUAL;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeIdentityRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeIdentityRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/NodeIdentityRule.java	(revision 922)
@@ -0,0 +1,32 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * This comparison rule returns <code>NodeEquality.IDENTICAL</code> if the comparison of the two
+ * task tree nodes using the <code>==</code> operator or the <code>equals</code> method return true.
+ * Else it returns null to denote, that it can not compare the nodes.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class NodeIdentityRule implements NodeComparisonRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if (node1 == node2) {
+            return NodeEquality.IDENTICAL;
+        }
+        else {
+            return null;
+        }
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SelectionComparisonRule.java	(revision 922)
@@ -0,0 +1,116 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * this node comparison rule is capable of comparing selections. If both selections do not have
+ * children, they are treated as identical. If they have children, each child of both selections
+ * is compared to each child of the respective other selection. The resulting equality is the most
+ * concrete one of all these comparisons. I.e. if all children are at least lexically equal, then
+ * the selections are lexically equal. If all children are at least syntactically equal, then the
+ * selections are syntactically equal. If all children are at least semantically equal, then the
+ * selections are semantically equal. If only one of the selections has children, then the
+ * selections are unequal.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class SelectionComparisonRule implements NodeComparisonRule {
+
+    /** the rule manager for internally comparing task tree nodes */
+    private NodeEqualityRuleManager mRuleManager;
+
+    /**
+     * <p>
+     * simple constructor to provide the rule with the node equality rule manager to be able
+     * to perform comparisons of the children of provided task tree nodes
+     * </p>
+     * 
+     * @param ruleManager the rule manager for comparing task tree nodes
+     */
+    SelectionComparisonRule(NodeEqualityRuleManager ruleManager) {
+        super();
+        mRuleManager = ruleManager;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if ((!(node1 instanceof ISelection)) || (!(node2 instanceof ISelection))) {
+            return null;
+        }
+
+        if (node1 == node2) {
+            return NodeEquality.IDENTICAL;
+        }
+
+        // if both sequences do not have children, they are identical. If only one of them has
+        // children, they are unequal.
+        if ((node1.getChildren().size() == 0) && (node2.getChildren().size() == 0)) {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+        else if ((node1.getChildren().size() == 0) || (node2.getChildren().size() == 0)) {
+            return NodeEquality.UNEQUAL;
+        }
+
+        NodeEquality selectionEquality = NodeEquality.LEXICALLY_EQUAL;
+
+        // compare each child of selection one with each child of selection two
+        NodeEquality childEquality;
+        NodeEquality currentEquality;
+        for (ITaskTreeNode child1 : node1.getChildren()) {
+            childEquality = null;
+            for (ITaskTreeNode child2 : node2.getChildren()) {
+                currentEquality = mRuleManager.applyRules(child1, child2);
+                if ((currentEquality != null) && (currentEquality != NodeEquality.UNEQUAL)) {
+                    if (childEquality == null) {
+                        childEquality = currentEquality;
+                    }
+                    else {
+                        childEquality = childEquality.getCommonDenominator(currentEquality);
+                    }
+                }
+            }
+            
+            if (childEquality != null) {
+                selectionEquality = selectionEquality.getCommonDenominator(childEquality);
+            }
+            else {
+                return NodeEquality.UNEQUAL;
+            }
+        }
+
+        // compare each child of selection two with each child of selection one
+        for (ITaskTreeNode child2 : node2.getChildren()) {
+            childEquality = null;
+            for (ITaskTreeNode child1 : node1.getChildren()) {
+                currentEquality = mRuleManager.applyRules(child1, child2);
+                if ((currentEquality != null) && (currentEquality != NodeEquality.UNEQUAL)) {
+                    if (childEquality == null) {
+                        childEquality = currentEquality;
+                    }
+                    else {
+                        childEquality = childEquality.getCommonDenominator(currentEquality);
+                    }
+                }
+            }
+            
+            if (childEquality != null) {
+                selectionEquality = selectionEquality.getCommonDenominator(childEquality);
+            }
+            else {
+                return NodeEquality.UNEQUAL;
+            }
+        }
+
+        return selectionEquality;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/nodeequality/SequenceComparisonRule.java	(revision 922)
@@ -0,0 +1,76 @@
+package de.ugoe.cs.autoquest.tasktrees.nodeequality;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * This rule is capable of comparing sequences. If both sequences do not have children, they are
+ * treated as lexically equal. Sequences are lexically equal, if they have the same number and
+ * order of lexically equal children. The rule can not decide, if two sequences are syntactically
+ * or semantically equal.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class SequenceComparisonRule implements NodeComparisonRule {
+
+    /** the rule manager for internally comparing task tree nodes */
+    private NodeEqualityRuleManager mRuleManager;
+
+    /**
+     * <p>
+     * simple constructor to provide the rule with the node equality rule manager to be able
+     * to perform comparisons of the children of provided task tree nodes
+     * </p>
+     * 
+     * @param ruleManager the rule manager for comparing task tree nodes
+     */
+    SequenceComparisonRule(NodeEqualityRuleManager ruleManager) {
+        super();
+        mRuleManager = ruleManager;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.nodeequality.NodeEqualityRule#apply(TaskTreeNode, TaskTreeNode)
+     */
+    @Override
+    public NodeEquality compare(ITaskTreeNode node1, ITaskTreeNode node2) {
+        if ((!(node1 instanceof ISequence)) || (!(node2 instanceof ISequence))) {
+            return null;
+        }
+
+        if (node1 == node2) {
+            return NodeEquality.IDENTICAL;
+        }
+
+        // if both sequences do not have children, they are equal although this doesn't make sense
+        if ((node1.getChildren().size() == 0) && (node2.getChildren().size() == 0)) {
+            return NodeEquality.LEXICALLY_EQUAL;
+        }
+
+        if (node1.getChildren().size() != node2.getChildren().size()) {
+            return NodeEquality.UNEQUAL;
+        }
+
+        NodeEquality resultingEquality = NodeEquality.LEXICALLY_EQUAL;
+        for (int i = 0; i < node1.getChildren().size(); i++) {
+            ITaskTreeNode child1 = node1.getChildren().get(i);
+            ITaskTreeNode child2 = node2.getChildren().get(i);
+
+            NodeEquality nodeEquality = mRuleManager.applyRules(child1, child2);
+
+            if ((nodeEquality == null) || (nodeEquality == NodeEquality.UNEQUAL)) {
+                return NodeEquality.UNEQUAL;
+            }
+            
+            resultingEquality = resultingEquality.getCommonDenominator(nodeEquality);
+        }
+
+        return resultingEquality;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultEventTargetSequenceDetectionRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultEventTargetSequenceDetectionRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultEventTargetSequenceDetectionRule.java	(revision 922)
@@ -0,0 +1,153 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.03.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DefaultEventTargetSequenceDetectionRule implements TemporalRelationshipRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.temporalrelation.TemporalRelationshipRule#apply(TaskTreeNode,
+     * TaskTreeBuilder, TaskTreeNodeFactory)
+     */
+    @Override
+    public RuleApplicationResult apply(ITaskTreeNode        parent,
+                                       ITaskTreeBuilder     builder,
+                                       ITaskTreeNodeFactory nodeFactory,
+                                       boolean              finalize)
+    {
+        if (!(parent instanceof ISequence)) {
+            return null;
+        }
+
+        RuleApplicationResult result = new RuleApplicationResult();
+
+        IEventTarget currentEventTarget = null;
+        int startingIndex = -1;
+
+        int index = 0;
+        while (index < parent.getChildren().size()) {
+            ITaskTreeNode child = parent.getChildren().get(index);
+
+            IEventTarget eventTarget = determineEventTarget(child);
+
+            if (((eventTarget == null) && (currentEventTarget != null)) ||
+                ((eventTarget != null) && (!eventTarget.equals(currentEventTarget))))
+            {
+                if (startingIndex < 0) {
+                    startingIndex = index;
+                    currentEventTarget = eventTarget;
+                }
+                else {
+                    int endIndex = index - 1;
+                    
+                    // only reduce to a sequence, if it is not a sequence with only one child
+                    // or if this child is not a sequence itself
+                    if ((startingIndex != endIndex) ||
+                        (!(parent.getChildren().get(startingIndex) instanceof ISequence)))
+                    {
+                        handleEventTargetSequence
+                            (parent, startingIndex, endIndex, builder, nodeFactory, result);
+
+                        result.setRuleApplicationStatus
+                            (RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+                        return result;
+                    }
+                    else {
+                        // here a new sequence on a new target begins
+                        startingIndex = index;
+                        currentEventTarget = eventTarget;
+                    }
+                }
+            }
+
+            index++;
+        }
+
+        if (startingIndex > -1) {
+            int endIndex = parent.getChildren().size() - 1;
+            
+            if (finalize) {
+                // only reduce to a sequence, if it is not a sequence with only one child
+                // or if this child is not a sequence itself
+                if ((startingIndex > 0) &&
+                    ((startingIndex != endIndex) ||
+                     (!(parent.getChildren().get(startingIndex) instanceof ISequence))))
+                {
+                    handleEventTargetSequence
+                        (parent, startingIndex, endIndex, builder, nodeFactory, result);
+                
+                    result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+                }
+            }
+            else {
+                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param child
+     * @return
+     */
+    private IEventTarget determineEventTarget(ITaskTreeNode node) {
+        if (node instanceof IEventTask) {
+            return ((IEventTask) node).getEventTarget();
+        }
+        else {
+            IEventTarget commonTarget = null;
+            
+            for (ITaskTreeNode child : node.getChildren()) {
+                if (commonTarget == null) {
+                    commonTarget = determineEventTarget(child);
+                }
+                else {
+                    if (!commonTarget.equals(determineEventTarget(child))) {
+                        return null;
+                    }
+                }
+            }
+            
+            return commonTarget;
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    private void handleEventTargetSequence(ITaskTreeNode         parent,
+                                           int                   startIndex,
+                                           int                   endIndex,
+                                           ITaskTreeBuilder      builder,
+                                           ITaskTreeNodeFactory  nodeFactory,
+                                           RuleApplicationResult result)
+    {
+        ISequence sequence = nodeFactory.createNewSequence();
+
+        for (int i = startIndex; i <= endIndex; i++) {
+            builder.addChild(sequence, parent.getChildren().get(startIndex));
+            builder.removeChild((ISequence) parent, startIndex);
+        }
+
+        builder.addChild((ISequence) parent, startIndex, sequence);
+
+        result.addNewlyCreatedParentNode(sequence);
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiElementSequenceDetectionRule.java	(revision 922)
@@ -0,0 +1,345 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.03.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DefaultGuiElementSequenceDetectionRule implements TemporalRelationshipRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.temporalrelation.TemporalRelationshipRule#apply(TaskTreeNode,
+     * TaskTreeBuilder, TaskTreeNodeFactory)
+     */
+    @Override
+    public RuleApplicationResult apply(ITaskTreeNode        parent,
+                                       ITaskTreeBuilder     builder,
+                                       ITaskTreeNodeFactory nodeFactory,
+                                       boolean              finalize)
+    {
+        if (!(parent instanceof ISequence)) {
+            return null;
+        }
+
+        RuleApplicationResult result = new RuleApplicationResult();
+        
+        IGUIElement lastGuiElement = null;
+        int index = 0;
+        while (index < parent.getChildren().size()) {
+            ITaskTreeNode child = parent.getChildren().get(index);
+            IGUIElement currentGuiElement = getGUIElement(child);
+            if ((index > 0) && (!lastGuiElement.equals(currentGuiElement))) {
+                ReducableCommonDenominator commonDenominator =
+                    getNextReducableCommonDenominator(parent, index - 1);
+                    
+                if (commonDenominator != null) {
+                    // condense only if not all children would be condensed or if we can be sure,
+                    // that there will be no further child that should be included in the condensed
+                    // sequence
+                    if ((commonDenominator.noOfTasks < parent.getChildren().size()) &&
+                        (!isOnGuiElementPath(commonDenominator.commonGuiElement, currentGuiElement)))
+                    {
+                        condenseTasksToSequence(parent, index, commonDenominator.noOfTasks,
+                                                builder, nodeFactory, result);
+
+                        result.setRuleApplicationStatus
+                            (RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+                        return result;
+                    }
+                    else {
+                        // the common denominator is on the parent path of the next GUI element.
+                        // Therefore, the current sequences is not finished yet. So break up.
+                        result.setRuleApplicationStatus
+                            (RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+                    }
+                }
+            }
+
+            lastGuiElement = currentGuiElement;
+            index++;
+        }
+
+        ReducableCommonDenominator commonDenominator =
+            getNextReducableCommonDenominator(parent, parent.getChildren().size() - 1);
+        
+        if ((commonDenominator != null) &&
+            (commonDenominator.noOfTasks < parent.getChildren().size()))
+        {
+            if (finalize) {
+                condenseTasksToSequence
+                    (parent, index, commonDenominator.noOfTasks, builder, nodeFactory, result);
+                
+                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+                
+                return result;
+            }
+            else {
+                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param guiElement
+     * @param detectedTasks
+     * @param parent
+     * @param index
+     * @param builder
+     * @param nodeFactory
+     * @return
+     */
+    private void condenseTasksToSequence(ITaskTreeNode         parent,
+                                         int                   parentIndex,
+                                         int                   noOfTasks,
+                                         ITaskTreeBuilder      builder,
+                                         ITaskTreeNodeFactory  nodeFactory,
+                                         RuleApplicationResult result)
+    {
+        ISequence newSequence = nodeFactory.createNewSequence();
+        for (int i = 0; i < noOfTasks; i++) {
+            builder.addChild(newSequence, parent.getChildren().get(parentIndex - noOfTasks));
+            // remove exactly the same number of children from the parent.
+            builder.removeChild((ISequence) parent, parentIndex - noOfTasks);
+        }
+                
+        builder.addChild((ISequence) parent, parentIndex - noOfTasks, newSequence);
+        result.addNewlyCreatedParentNode(newSequence);
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param detectedTasks
+     * @return
+     */
+    private ReducableCommonDenominator getNextReducableCommonDenominator(ITaskTreeNode parent,
+                                                                         int           childIndex)
+    {
+        ReducableCommonDenominator commonDenominator = null;
+        
+        // a common denominator can only exist for at least two task tree nodes
+        if (childIndex > 0) {
+            // start with the last one
+            int pos = childIndex;
+
+            commonDenominator = new ReducableCommonDenominator();
+            
+            // check for further predecessors, if they match the same common denominator
+            IGUIElement currentCommonDenominator = null;
+            do {
+                if (--pos < 0) {
+                    currentCommonDenominator = null;
+                }
+                else {
+                    currentCommonDenominator = getCommonDenominator
+                        (getGUIElement(parent.getChildren().get(pos)),
+                         getGUIElement(parent.getChildren().get(pos + 1)));
+                }
+                
+                if (commonDenominator.commonGuiElement == null) {
+                    commonDenominator.commonGuiElement = currentCommonDenominator;
+                }
+            }
+            while ((commonDenominator.commonGuiElement != null) &&
+                   (commonDenominator.commonGuiElement.equals(currentCommonDenominator)));
+            
+            if (commonDenominator.commonGuiElement != null) {
+                // pos points to the last element, that has not the same common denominator.
+                // This one must be subtracted from the task number as well
+                commonDenominator.noOfTasks = childIndex - pos;
+            }
+            else {
+                commonDenominator = null;
+            }
+        }
+        
+        return commonDenominator;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private IGUIElement getGUIElement(ITaskTreeNode node) {
+        List<IGUIElement> terminalGUIElements = new ArrayList<IGUIElement>();
+        getTerminalGUIElements(node, terminalGUIElements);
+        return getCommonDenominator(terminalGUIElements);
+    }
+        
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param detectedTaskGroups
+     * @return
+     */
+    /*private IGUIElement getCommonDenominator(Stack<Task> detectedTasks, int start) {
+        List<IGUIElement> allGUIElements = new ArrayList<IGUIElement>();
+        
+        for (int i = start; i < detectedTasks.size(); i++) {
+            allGUIElements.add(detectedTasks.get(i).commonGuiElement);
+        }
+        
+        return getCommonDenominator(allGUIElements);
+    }*/
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private IGUIElement getCommonDenominator(IGUIElement guiElement1, IGUIElement guiElement2) {
+        List<IGUIElement> allGUIElements = new ArrayList<IGUIElement>();
+        allGUIElements.add(guiElement1);
+        allGUIElements.add(guiElement2);
+        return getCommonDenominator(allGUIElements);
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private IGUIElement getCommonDenominator(List<IGUIElement> guiElements) {
+        IGUIElement commonDenominator = null;
+        
+        if (guiElements.size() > 0) {
+            List<IGUIElement> commonDenominatorPath = new ArrayList<IGUIElement>();
+            
+            // create a reference list using the first GUI element
+            IGUIElement guiElement = guiElements.get(0);
+            while (guiElement != null) {
+                commonDenominatorPath.add(0, guiElement);
+                guiElement = guiElement.getParent();
+            }
+            
+            // for each other GUI element, check the reference list for the first element in the
+            // path, that is not common to the current one, and delete it as well as it subsequent
+            // siblings
+            List<IGUIElement> currentPath = new ArrayList<IGUIElement>();
+            for (int i = 1; i < guiElements.size(); i++) {
+                currentPath.clear();
+                guiElement = guiElements.get(i);
+                while (guiElement != null) {
+                    currentPath.add(0, guiElement);
+                    guiElement = guiElement.getParent();
+                }
+                
+                // determine the index of the first unequal path element
+                int index = 0;
+                while ((index < commonDenominatorPath.size()) && (index < currentPath.size()) &&
+                        commonDenominatorPath.get(index).equals(currentPath.get(index)))
+                {
+                    index++;
+                }
+                
+                // remove all elements from the common denonimator path, that do not match
+                while (index < commonDenominatorPath.size()) {
+                    commonDenominatorPath.remove(index);
+                }
+            }
+            
+            if (commonDenominatorPath.size() > 0) {
+                commonDenominator = commonDenominatorPath.get(commonDenominatorPath.size() - 1);
+            }
+        }
+        
+        return commonDenominator;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param child
+     * @return
+     */
+    private void getTerminalGUIElements(ITaskTreeNode node, List<IGUIElement> terminalGUIElements) {
+        if (node instanceof IEventTask) {
+            if (((IEventTask) node).getEventTarget() instanceof IGUIElement) {
+                terminalGUIElements.add((IGUIElement) ((IEventTask) node).getEventTarget());
+            }
+        }
+        else {
+            for (ITaskTreeNode child : node.getChildren()) {
+                getTerminalGUIElements(child, terminalGUIElements);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param currentCommonDenominator
+     * @param guiElement
+     * @return
+     */
+    private boolean isOnGuiElementPath(IGUIElement potentialPathElement, IGUIElement child) {
+        IGUIElement guiElement = child;
+        
+        while (guiElement != null) {
+            if (guiElement.equals(potentialPathElement)) {
+                return true;
+            }
+            guiElement = guiElement.getParent();
+        }
+        
+        return false;
+    }
+
+    /**
+     * 
+     */
+    private static class ReducableCommonDenominator {
+        
+        /** the GUI element being the common denominator */
+        private IGUIElement commonGuiElement;
+        
+        /** the number of tasks that match the common denominator */
+        private int noOfTasks;
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return noOfTasks + " tasks on " + commonGuiElement;
+        }
+        
+    }
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiEventSequenceDetectionRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiEventSequenceDetectionRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultGuiEventSequenceDetectionRule.java	(revision 922)
@@ -0,0 +1,118 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.03.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DefaultGuiEventSequenceDetectionRule implements TemporalRelationshipRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.temporalrelation.TemporalRelationshipRule#apply(TaskTreeNode,
+     * TaskTreeBuilder, TaskTreeNodeFactory)
+     */
+    @Override
+    public RuleApplicationResult apply(ITaskTreeNode        parent,
+                                       ITaskTreeBuilder     builder,
+                                       ITaskTreeNodeFactory nodeFactory,
+                                       boolean              finalize)
+    {
+        if (!(parent instanceof ISequence)) {
+            return null;
+        }
+
+        RuleApplicationResult result = new RuleApplicationResult();
+        int sequenceStartingIndex = -1;
+
+        int index = 0;
+        while (index < parent.getChildren().size()) {
+            ITaskTreeNode child = parent.getChildren().get(index);
+
+            if ((child instanceof IEventTask) &&
+                (((IEventTask) child).getEventType() instanceof IInteraction))
+            {
+                IInteraction eventType = (IInteraction) ((IEventTask) child).getEventType();
+                
+                if (eventType.finishesLogicalSequence() && (sequenceStartingIndex > -1))
+                {
+                    // There are several situations in which this implementation may cause infinite
+                    // loops. This is because the rule manager will reapply rules until
+                    // no rule is applied anymore. A sequence identified in a first iteration will
+                    // be identified as a sequence also in a second iteration. As an example
+                    // many sequences start with an interaction starting that sequence and end
+                    // with an interaction ending that sequence. This will be reidentified as
+                    // further subsequence. It must therefore be assured, that a sequence, that
+                    // was once identified is not reidentified in a further application of the rule.
+                    // For this, the implementation performs a kind of dry run. It creates a list of
+                    // children that would belong to an identified sequence. Only if this list is
+                    // not a reidentification then a new sequence is created and added to the
+                    // parent. If it is a reidentification can be identified, if the list of
+                    // children will contain all children of the parent, or if the list of children
+                    // only consists of one sequence. Further, an identified sequence must at least
+                    // have one child.
+                    if (((sequenceStartingIndex != 0) ||
+                         (index != (parent.getChildren().size() - 1))) &&
+                        (((index - sequenceStartingIndex) > 0) ||
+                          (((index - sequenceStartingIndex) == 0) &&
+                           (!eventType.startsLogicalSequence()))))
+                    {
+                        boolean allNewChildrenAreSequences = true;
+
+                        for (int j = sequenceStartingIndex;
+                             ((allNewChildrenAreSequences) && (j < index)); j++)
+                        {
+                            allNewChildrenAreSequences &=
+                                (parent.getChildren().get(j) instanceof ISequence);
+                        }
+
+                        if (!allNewChildrenAreSequences) {
+                            ISequence sequence = nodeFactory.createNewSequence();
+
+                            for (int j = sequenceStartingIndex; j < index; j++) {
+                                builder.addChild
+                                    (sequence, parent.getChildren().get(sequenceStartingIndex));
+                                builder.removeChild((ISequence) parent, sequenceStartingIndex);
+                            }
+
+                            if (!eventType.startsLogicalSequence()) {
+                                builder.addChild
+                                    (sequence, parent.getChildren().get(sequenceStartingIndex));
+                                builder.removeChild((ISequence) parent, sequenceStartingIndex);
+                            }
+
+                            builder.addChild((ISequence) parent, sequenceStartingIndex, sequence);
+
+                            result.addNewlyCreatedParentNode(sequence);
+                            result.setRuleApplicationStatus
+                                (RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+                            return result;
+                        }
+                    }
+                }
+
+                if (eventType.startsLogicalSequence()) {
+                    sequenceStartingIndex = index;
+                }
+            }
+
+            index++;
+        }
+
+        if (sequenceStartingIndex >= 0) {
+            result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+        }
+
+        return result;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultIterationDetectionRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultIterationDetectionRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/DefaultIterationDetectionRule.java	(revision 922)
@@ -0,0 +1,727 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * <p>
+ * iterations in a list of nodes are equal subsequences following each other directly. The
+ * subsequences can be of any length depending on the type of equality they need to have. If the
+ * subsequences have to be lexically equal, then they have to have the same length if they only
+ * contain event tasks. As an example entering text can be done through appropriate keystrokes or
+ * through pasting the text. As a result, two syntactically different sequences are semantically
+ * equal. If both follow each other, then they are an iteration of semantically equal children.
+ * But they are not lexically equal.
+ * </p>
+ * <p>
+ * This class determines equal subsequences following each other. It is provided with a minimal node
+ * equality the equal nodes should have. Through this, it is possible to find e.g. lexically
+ * equal subsequence through a first application of this rule and semantically equal children to 
+ * a later application of this rule. This is used by the {@link TemporalRelationshipRuleManager}
+ * which instantiates this rule three times, each with a different minimal equality.
+ * </p>
+ * <p>
+ * The equal subsequences are determined through trial and error. This algorithm has a high effort
+ * as it tries in the worst case all possible combinations of sub lists in all possible parts of
+ * the list of children of a provided parent node. The steps for each trial are.
+ * <ul>
+ *   <li>for all possible subparts of the children of the provided parent
+ *   <ul>
+ *     <li>for all possible first sublists in the subpart
+ *     <ul>
+ *       <li>for all succeeding next sublists in this part</li>
+ *       <ul>
+ *         <li>check if this sublist is equal to all previously identified sublist in this part</li>
+ *       </ul>
+ *     </ul>
+ *     <li>
+ *       if a combination of sublists is found in this subpart which are all equal to each other
+ *       at the provided minimal equality level, an iteration in this subpart was found.
+ *     </li>
+ *       <ul>
+ *         <li>merge the identified equal sublists to an iteration</li>
+ *       </ul>
+ *   </ul>
+ * </ul>
+ * The algorithm tries to optimize if all children are event tasks and if the sublists shall be
+ * lexically equal. In this case, the sublist all have to have the same length. The trial and
+ * error reduces to a minimum of possible sublists.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+public class DefaultIterationDetectionRule implements TemporalRelationshipRule {
+    
+    /**
+     * <p>
+     * the node equality manager needed for comparing task tree nodes with each other
+     * </p>
+     */
+    private NodeEqualityRuleManager nodeEqualityRuleManager;
+
+    /**
+     * <p>
+     * the minimal node equality two identified sublists need to have to consider them as equal
+     * and to create an iteration for
+     * </p>
+     */
+    private NodeEquality minimalNodeEquality;
+
+    /**
+     * <p>
+     * instantiates the rule and initializes it with a node equality rule manager and the minimal
+     * node equality identified sublist must have to consider them as iterated.
+     * </p>
+     */
+    DefaultIterationDetectionRule(NodeEqualityRuleManager nodeEqualityRuleManager,
+                                  NodeEquality            minimalNodeEquality)
+    {
+        super();
+        this.nodeEqualityRuleManager = nodeEqualityRuleManager;
+        this.minimalNodeEquality = minimalNodeEquality;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see TemporalRelationshipRule#apply(TaskTreeNode, TaskTreeBuilder, TaskTreeNodeFactory)
+     */
+    @Override
+    public RuleApplicationResult apply(ITaskTreeNode        parent,
+                                       ITaskTreeBuilder     treeBuilder,
+                                       ITaskTreeNodeFactory nodeFactory,
+                                       boolean              finalize)
+    {
+        if (!(parent instanceof ISequence)) {
+            return null;
+        }
+
+        if (!finalize) {
+            // the rule is always feasible as iterations may occur at any time
+            RuleApplicationResult result = new RuleApplicationResult();
+            result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+            return result;
+        }
+
+        // parent must already have at least 2 children
+        if ((parent.getChildren() == null) || (parent.getChildren().size() < 2)) {
+            return null;
+        }
+        
+        
+        // to find longer iterations first, start with long sequences
+        SubSequences subSequences = getEqualSubsequences(parent, treeBuilder, nodeFactory);
+
+        if (subSequences != null) {
+            RuleApplicationResult result = new RuleApplicationResult();
+
+            mergeEqualNodes(subSequences.equalVariants, treeBuilder, nodeFactory);
+            IIteration newIteration = createIterationBasedOnIdentifiedVariants
+                (subSequences, treeBuilder, nodeFactory, result);
+
+            determineNewlyCreatedParentTasks(parent, newIteration, result);
+            
+            // remove iterated children
+            for (int j = subSequences.start; j < subSequences.end; j++) {
+                treeBuilder.removeChild((ISequence) parent, subSequences.start);
+            }
+
+            // add the new iteration instead
+            treeBuilder.addChild((ISequence) parent, subSequences.start, newIteration);
+
+            result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+            return result;
+        }
+
+        return null;
+    }
+
+    /**
+     * <p>
+     * this method initiates the trial and error algorithm denoted in the description of this class.
+     * Its main purpose is the selection of a subpart of all children in the parent node in which
+     * equal sublists shall be searched. It is important, to always find the last iterations in a
+     * part first. The reason for this are iterations of iterations. If we always found the first
+     * iteration in a subpart first, then this may be an iteration of iterations. However, there
+     * may be subsequent iterations to be included in this iteration. But these iterations are not
+     * found yet, as they occur later in the sequence. Therefore, if we always find the last
+     * iteration in a sequence first, iterations of iterations are identified, last.
+     * </p>
+     * 
+     * @param parent      the parent node in which iterations of children shall be found
+     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
+     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
+     * 
+     * @return the iterated subsequences identified in a specific part (contains the equal
+     *         subsequences as well as the start (inclusive) and end (exclusive) index of the
+     *         subpart in which the sequences were found) 
+     */
+    private SubSequences getEqualSubsequences(ITaskTreeNode        parent,
+                                              ITaskTreeBuilder     treeBuilder,
+                                              ITaskTreeNodeFactory nodeFactory)
+    {
+        SubSequences subSequences = null;
+
+        FIND_ITERATION:
+        for (int end = parent.getChildren().size(); end > 0; end--) {
+            for (int start = 0; start < end; start++) {
+                boolean useEqualSublistLengths = equalSublistLengthsCanBeUsed(parent, start, end);
+
+                subSequences = new SubSequences();
+                subSequences.start = start;
+
+                boolean foundFurtherVariants = findFurtherVariants
+                    (subSequences, parent, start, end, treeBuilder, nodeFactory,
+                     useEqualSublistLengths);
+
+                if (foundFurtherVariants) {
+                    break FIND_ITERATION;
+                }
+                else {
+                    subSequences = null;
+                }
+            }
+        }
+        
+        return subSequences;
+    }
+
+    /**
+     * <p>
+     * for optimization purposes, we check if the length of the sublists to be identified as
+     * iterations has to be the same for any sublist. This only applies, if the minimum node
+     * equality to be checked for is lexical equality. If the children of the parent are all event
+     * tasks, then sublists can only be lexically equal, if they all have the same length.
+     * Therefore we check, if the minimal node equality is lexical equality. And if so, we also
+     * check if all children of the parent in which an iteration shall be searched for are event
+     * tasks.
+     * </p>
+     *
+     * @param parent the parent node to search for iterations of its children
+     * @param start  the beginning of the subpart (inclusive) to be considered
+     * @param end    the end of the subpart (exclusive) to be considered
+     * 
+     * @return true, if the sublists must have the same lengths, false else
+     */
+    private boolean equalSublistLengthsCanBeUsed(ITaskTreeNode parent, int start, int end) {
+        boolean equalLengthsCanBeUsed = minimalNodeEquality.isAtLeast(NodeEquality.LEXICALLY_EQUAL);
+        
+        if (equalLengthsCanBeUsed) {
+            for (int i = start; i < end; i++) {
+                if (!(parent.getChildren().get(i) instanceof IEventTask)) {
+                    equalLengthsCanBeUsed = false;
+                    break;
+                }
+            }
+        }
+
+        return equalLengthsCanBeUsed;
+    }
+
+    /**
+     * <p>
+     * this method starts at a specific position in the list of children of the provided parent
+     * and checks, if it finds a further sublist, that matches the already found sublists. If
+     * the sublist lengths must be equal, it only searches for a sublist of the same length of the
+     * already found sublists. The method calls itself if it identifies a further equal sublist but
+     * if the end of the subpart of children is not yet reached.
+     * </p>
+     * 
+     * @param subSequences           the sublist found so far against which equality of the next
+     *                               sublist must be checked
+     * @param parent                 the parent node of which the children are analyzed
+     * @param start                  the starting index from which to start the next sublist to be
+     *                               identified
+     * @param end                    the end index (exclusive) of the current subpart of children
+     *                               in which iterations are searched for
+     * @param treeBuilder            the tree builder that can be used for connecting task tree
+     *                               nodes
+     * @param nodeFactory            the node factory that can be used for instantiating task tree
+     *                               nodes
+     * @param useEqualSublistLengths true if the sublists to be searched for all need to have the
+     *                               same length
+     * 
+     * @return true if a further equal variant was found, false else
+     */
+    private boolean findFurtherVariants(SubSequences         subSequences,
+                                        ITaskTreeNode        parent,
+                                        int                  start,
+                                        int                  end,
+                                        ITaskTreeBuilder     treeBuilder,
+                                        ITaskTreeNodeFactory nodeFactory,
+                                        boolean              useEqualSublistLengths)
+    {
+        boolean foundFurtherVariants = (start == end) && (subSequences.equalVariants.size() > 1);
+        
+        int minChildCount = 1;
+        int maxChildCount = end - start;
+        
+        if (useEqualSublistLengths && (subSequences.equalVariants.size() > 0)) {
+            minChildCount = subSequences.equalVariants.get(0).getChildren().size();
+            maxChildCount = Math.min(minChildCount, maxChildCount);
+        }
+        
+        for (int childCount = minChildCount; childCount <= maxChildCount; childCount++) {
+            if (useEqualSublistLengths && (((end - start) % childCount) != 0)) {
+                continue;
+            }
+            
+            ISequence furtherVariant = nodeFactory.createNewSequence();
+            
+            for (int j = start; j < start + childCount; j++) {
+                treeBuilder.addChild(furtherVariant, parent.getChildren().get(j));
+            }
+            
+            boolean allVariantsEqual = true;
+            
+            for (ITaskTreeNode equalVariant : subSequences.equalVariants) {
+                NodeEquality nodeEquality =
+                    nodeEqualityRuleManager.applyRules(equalVariant, furtherVariant);
+                
+                if (!nodeEquality.isAtLeast(minimalNodeEquality)) {
+                    allVariantsEqual = false;
+                    break;
+                }
+            }
+            
+            if (allVariantsEqual) {
+                
+                // we found a further variant. Add it to the list of variants and try to find
+                // further variants. Ignore, if none is available
+                int index = subSequences.equalVariants.size();
+                subSequences.equalVariants.add(index, furtherVariant);
+                
+                foundFurtherVariants = findFurtherVariants
+                    (subSequences, parent, start + childCount, end, treeBuilder, nodeFactory,
+                     useEqualSublistLengths);
+
+                if (foundFurtherVariants) {
+                    subSequences.end = end;
+                    break;
+                }
+                else {
+                    subSequences.equalVariants.remove(index);
+                }
+            }
+        }
+        
+        return foundFurtherVariants;
+    }
+
+    /**
+     * <p>
+     * this method merges task tree nodes in a list, if they can be merged. for this, it tries
+     * to merge every node with every other node in the provided list using the
+     * {@link #mergeEqualTasks(ITaskTreeNode, ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory)}
+     * method. If a merge is possible, it removes the merged nodes from the list and adds the
+     * merge result. 
+     * </p>
+     *
+     * @param nodes       the list of nodes to be merged
+     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
+     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
+     */
+    private void mergeEqualNodes(List<ITaskTreeNode>   nodes,
+                                 ITaskTreeBuilder      treeBuilder,
+                                 ITaskTreeNodeFactory  nodeFactory)
+    {
+        int index1 = 0;
+        int index2 = 0;
+        ITaskTreeNode variant1;
+        ITaskTreeNode variant2;
+        
+        while (index1 < nodes.size()) {
+            variant1 = nodes.get(index1);
+            index2 = index1 + 1;
+            
+            while (index2 < nodes.size()) {
+                variant2 = nodes.get(index2);
+                ITaskTreeNode mergedChild =
+                    mergeEqualTasks(variant1, variant2, treeBuilder, nodeFactory);
+                
+                if (mergedChild != null) {
+                    // if we merged something start from the beginning to perform the next merge
+                    nodes.remove(index2);
+                    nodes.remove(index1);
+                    nodes.add(index1, mergedChild);
+                    index1 = -1;
+                    break;
+                }
+                else {
+                    index2++;
+                }
+            }
+            
+            index1++;
+        }
+    }
+
+    /**
+     * <p>
+     * this method merges two equal tasks with each other if possible. If the tasks are lexically
+     * equal, the first of them is returned as merge result. If both tasks are of the same
+     * temporal relationship type, the appropriate merge method is called to merge them. If one
+     * of the nodes is a selection, the other one is added as a variant of this selection.
+     * (However, if both nodes are selections, they are merged using the appropriate merge method.)
+     * If merging is not possible, then a selection of both provided nodes is created and
+     * returned as merge result.
+     * </p>
+     *
+     * @param node1       the first task to be merged
+     * @param node2       the second task to be merged
+     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
+     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
+     * 
+     * @return the result of the merge
+     */
+    private ITaskTreeNode mergeEqualTasks(ITaskTreeNode         node1,
+                                          ITaskTreeNode         node2,
+                                          ITaskTreeBuilder      treeBuilder,
+                                          ITaskTreeNodeFactory  nodeFactory)
+    {
+        ITaskTreeNode mergeResult = null;
+        
+        if ((node1 instanceof ISequence) && (node2 instanceof ISequence)) {
+            mergeResult = mergeEqualSequences
+                ((ISequence) node1, (ISequence) node2, treeBuilder, nodeFactory);
+        }
+        else if ((node1 instanceof ISelection) && (node2 instanceof ISelection)) {
+            mergeResult = mergeEqualSelections
+                ((ISelection) node1, (ISelection) node2, treeBuilder, nodeFactory);
+        }
+        else if ((node1 instanceof IIteration) && (node2 instanceof IIteration)) {
+            mergeResult = mergeEqualIterations
+                ((IIteration) node1, (IIteration) node2, treeBuilder, nodeFactory);
+        }
+        else if (node1 instanceof ISelection) {
+            treeBuilder.addChild((ISelection) node1, node2);
+            mergeResult = node1;
+        }
+        else if (node2 instanceof ISelection) {
+            treeBuilder.addChild((ISelection) node2, node1);
+            mergeResult = node2;
+        }
+        else if (node1 instanceof IIteration) {
+            mergeResult = mergeEqualTasks
+                (((IIteration) node1).getChildren().get(0), node2, treeBuilder, nodeFactory);
+            
+            if (mergeResult != null) {
+                IIteration iteration = nodeFactory.createNewIteration();
+                treeBuilder.setChild(iteration, mergeResult);
+                mergeResult = iteration;
+            }
+        }
+        else if (node2 instanceof IIteration) {
+            mergeResult = mergeEqualTasks
+                (((IIteration) node2).getChildren().get(0), node1, treeBuilder, nodeFactory);
+            
+            if (mergeResult != null) {
+                IIteration iteration = nodeFactory.createNewIteration();
+                treeBuilder.setChild(iteration, mergeResult);
+                mergeResult = iteration;
+            }
+        }
+        else {
+            NodeEquality nodeEquality = nodeEqualityRuleManager.applyRules(node1, node2);
+            
+            if (nodeEquality.isAtLeast(NodeEquality.LEXICALLY_EQUAL)) {
+                mergeResult = node1;
+            }
+        }
+
+        if (mergeResult == null) {
+            mergeResult = nodeFactory.createNewSelection();
+            treeBuilder.addChild((ISelection) mergeResult, node1);
+            treeBuilder.addChild((ISelection) mergeResult, node2);
+        }
+        
+        return mergeResult;
+    }
+
+    /**
+     * <p>
+     * merges equal sequences. This is done through trying to merge each node of sequence 1 with
+     * the node in sequence 2 being located at the same position. If not all children can be merged
+     * or if the sequences have different lengths, null is returned to indicate, that merging is
+     * not possible. For merging children, the
+     * {@link #mergeEqualTasks(ITaskTreeNode, ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory)}
+     * method is called.
+     * </p>
+     *
+     * @param sequence1   the first sequence to be merged
+     * @param sequence2   the second sequence to be merged
+     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
+     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
+     * 
+     * @return the result of the merge or null if merging was not possible
+     */
+    private ISequence mergeEqualSequences(ISequence             sequence1,
+                                          ISequence             sequence2,
+                                          ITaskTreeBuilder      treeBuilder,
+                                          ITaskTreeNodeFactory  nodeFactory)
+    {
+        ISequence mergeResult = null;
+        
+        if (sequence1.getChildren().size() == sequence2.getChildren().size()) {
+            mergeResult = nodeFactory.createNewSequence();
+            
+            for (int i = 0; i < sequence1.getChildren().size(); i++) {
+                ITaskTreeNode mergedNode = mergeEqualTasks
+                    (sequence1.getChildren().get(i), sequence2.getChildren().get(i),
+                     treeBuilder, nodeFactory);
+                
+                if (mergedNode != null) {
+                    treeBuilder.addChild(mergeResult, mergedNode);
+                }
+                else {
+                    mergeResult = null;
+                    break;
+                }
+            }
+        }
+        
+        return mergeResult;
+    }
+
+    /**
+     * <p>
+     * merges equal selections. This is done through trying to merge each node of selections with
+     * each other. For this, the method
+     * {@link #mergeEqualNodes(List, ITaskTreeBuilder, ITaskTreeNodeFactory)} is called with a
+     * join of the child list of both selections.
+     * </p>
+     *
+     * @param selection1  the first selection to be merged
+     * @param selection2  the second selection to be merged
+     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
+     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
+     * 
+     * @return the result of the merge which is not null
+     */
+    private ITaskTreeNode mergeEqualSelections(ISelection            selection1,
+                                               ISelection            selection2,
+                                               ITaskTreeBuilder      treeBuilder,
+                                               ITaskTreeNodeFactory  nodeFactory)
+    {
+        ISelection mergeResult = nodeFactory.createNewSelection();
+            
+        for (int i = 0; i < selection1.getChildren().size(); i++) {
+            treeBuilder.addChild(mergeResult, selection1.getChildren().get(i));
+        }
+        
+        for (int i = 0; i < selection2.getChildren().size(); i++) {
+            treeBuilder.addChild(mergeResult, selection2.getChildren().get(i));
+        }
+        
+        mergeEqualNodes(mergeResult.getChildren(), treeBuilder, nodeFactory);
+        
+        return mergeResult;
+    }
+
+    /**
+     * <p>
+     * merges equal iterations. This is done through merging the children of both iterations. If
+     * this is possible, a resulting iteration with the merge result of the children as its own
+     * child is returned. Otherwise null is returned to indicate that merging was not possible.
+     * </p>
+     *
+     * @param selection1  the first iteration to be merged
+     * @param selection2  the second iteration to be merged
+     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
+     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
+     * 
+     * @return the result of the merge or null if merging is not possible
+     */
+    private ITaskTreeNode mergeEqualIterations(IIteration            iteration1,
+                                               IIteration            iteration2,
+                                               ITaskTreeBuilder      treeBuilder,
+                                               ITaskTreeNodeFactory  nodeFactory)
+    {
+        ITaskTreeNode mergedChild = mergeEqualTasks
+            (iteration1.getChildren().get(0), iteration2.getChildren().get(0),
+             treeBuilder, nodeFactory);
+        
+        IIteration mergeResult = null;
+        
+        if (mergedChild != null) {
+            mergeResult = nodeFactory.createNewIteration();
+            treeBuilder.setChild(mergeResult, mergedChild);
+        }
+        
+        return mergeResult;
+    }
+
+    /**
+     * <p>
+     * this is a convenience method to create an iteration based on the identified and already
+     * merged iterated subsequences. This method creates the simplest iteration possible. As an
+     * example, if always the same task tree node is iterated, it becomes the child of the
+     * iteration. If a sequence of tasks is iterated, this sequence becomes the child of the
+     * iteration. It several equal sublists or nodes which are not lexically equal are iterated
+     * they become a selection which in turn become the child of the iteration.
+     * </p>
+     *
+     * @param subsequences the identified and already merged equal subsequences
+     * @param treeBuilder  the tree builder that can be used for connecting task tree nodes
+     * @param nodeFactory  the node factory that can be used for instantiating the iteration
+     * 
+     * @return the resulting iteration
+     */
+    private IIteration createIterationBasedOnIdentifiedVariants(SubSequences          subsequences,
+                                                                ITaskTreeBuilder      treeBuilder,
+                                                                ITaskTreeNodeFactory  nodeFactory,
+                                                                RuleApplicationResult result)
+    {
+        IIteration newIteration = nodeFactory.createNewIteration();
+        result.addNewlyCreatedParentNode(newIteration);
+
+        if (subsequences.equalVariants.size() == 1) {
+            // all children are the same. Create an iteration of this child
+            if (subsequences.equalVariants.get(0).getChildren().size() == 1) {
+                // there is only one equal variant and this has only one child. So create an
+                // iteration of this child
+                treeBuilder.setChild
+                    (newIteration, subsequences.equalVariants.get(0).getChildren().get(0));
+            }
+            else {
+                // there was an iteration of one equal sequence
+                treeBuilder.setChild(newIteration, subsequences.equalVariants.get(0));
+                result.addNewlyCreatedParentNode(subsequences.equalVariants.get(0));
+            }
+        }
+        else {
+            // there are distinct variants of equal subsequences or children --> create an
+            // iterated selection
+            ISelection selection = nodeFactory.createNewSelection();
+            result.addNewlyCreatedParentNode(selection);
+
+            for (ITaskTreeNode variant : subsequences.equalVariants) {
+                if (variant.getChildren().size() == 1) {
+                    treeBuilder.addChild(selection, variant.getChildren().get(0));
+                }
+                else {
+                    treeBuilder.addChild(selection, variant);
+                    result.addNewlyCreatedParentNode(variant);
+                }
+            }
+
+            treeBuilder.setChild(newIteration, selection);
+        }
+        
+        return newIteration;
+    }
+
+    /**
+     * <p>
+     * as the method has to denote all newly created parent nodes this method identifies them by
+     * comparing the existing subtree with the newly created iteration. Only those parent nodes
+     * in the new iteration, which are not already found in the existing sub tree are denoted as
+     * newly created. We do this in this way, as during the iteration detection algorithm, many
+     * parent nodes are created, which may be discarded later. It is easier to identify the
+     * remaining newly created parent nodes through this way than to integrate it into the
+     * algorithm.
+     * </p>
+     * 
+     * @param existingSubTree the existing subtree
+     * @param newSubTree      the identified iteration
+     * @param result          the rule application result into which the newly created parent nodes
+     *                        shall be stored.
+     */
+    private void determineNewlyCreatedParentTasks(ITaskTreeNode         existingSubTree,
+                                                  ITaskTreeNode         newSubTree,
+                                                  RuleApplicationResult result)
+    {
+        List<ITaskTreeNode> existingParentNodes = getParentNodes(existingSubTree);
+        List<ITaskTreeNode> newParentNodes = getParentNodes(newSubTree);
+        
+        boolean foundNode;
+        for (ITaskTreeNode newParentNode : newParentNodes) {
+            foundNode = false;
+            for (ITaskTreeNode existingParentNode : existingParentNodes) {
+                // It is sufficient to compare the references. The algorithm reuses nodes as they
+                // are. So any node existing in the new structure that is also in the old structure
+                // was unchanged an therefore does not need to be handled as a newly created one.
+                // but every node in the new structure that is not included in the old structure
+                // must be treated as a newly created one.
+                if (newParentNode == existingParentNode) {
+                    foundNode = true;
+                    break;
+                }
+            }
+            
+            if (!foundNode) {
+                result.addNewlyCreatedParentNode(newParentNode);
+            }
+        }
+        
+    }
+
+    /**
+     * <p>
+     * convenience method to determine all parent nodes existing in a subtree
+     * </p>
+     *
+     * @param subtree the subtree to search for parent nodes in
+     * 
+     * @return a list of parent nodes existing in the subtree
+     */
+    private List<ITaskTreeNode> getParentNodes(ITaskTreeNode subtree) {
+        List<ITaskTreeNode> parentNodes = new ArrayList<ITaskTreeNode>();
+        
+        if (subtree.getChildren().size() > 0) {
+            parentNodes.add(subtree);
+            
+            for (ITaskTreeNode child : subtree.getChildren()) {
+                parentNodes.addAll(getParentNodes(child));
+            }
+        }
+        
+        return parentNodes;
+    }
+
+    /**
+     * <p>
+     * used to have a container for equal sublists identified in a sub part of the children of
+     * a parent node.
+     * </p>
+     * 
+     * @author Patrick Harms
+     */
+    private static class SubSequences {
+
+        /**
+         * <p>
+         * the beginning of the subpart of the children of the parent node in which the sublists
+         * are found (inclusive)
+         * </p>
+         */
+        public int start;
+        
+        /**
+         * <p>
+         * the end of the subpart of the children of the parent node in which the sublists
+         * are found (exclusive)
+         * </p>
+         */
+        public int end;
+        
+        /**
+         * <p>
+         * the equal sublists found in the subpart of the children of the parent node
+         * </p>
+         */
+        List<ITaskTreeNode> equalVariants = new ArrayList<ITaskTreeNode>();
+        
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/RuleApplicationResult.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/RuleApplicationResult.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/RuleApplicationResult.java	(revision 922)
@@ -0,0 +1,71 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * <p>
+ * The rule application result describes the result of applying a {@link TemporalRelationshipRule}
+ * on a task tree node. It contains a {@link RuleApplicationStatus} and a list of all parent
+ * task tree nodes that were created during a rule application. See the description of
+ * {@link TemporalRelationshipRule} for more details.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+class RuleApplicationResult {
+
+    /** */
+    private RuleApplicationStatus status = RuleApplicationStatus.RULE_NOT_APPLIED;
+
+    /** */
+    private List<ITaskTreeNode> newParents = new ArrayList<ITaskTreeNode>();
+
+    /**
+     * <p>
+     * create a rule application result with a status {@link RuleApplicationStatus#RULE_NOT_APPLIED}
+     * </p>
+     */
+    RuleApplicationResult() {
+        // this is the default indicating nothing so far
+    }
+
+    /**
+     * <p>
+     * set the rule application status
+     * </p>
+     */
+    void setRuleApplicationStatus(RuleApplicationStatus status) {
+        this.status = status;
+    }
+
+    /**
+     * <p>
+     * return the rule application status
+     * </p>
+     */
+    RuleApplicationStatus getRuleApplicationStatus() {
+        return status;
+    }
+
+    /**
+     * <p>
+     * add a further parent node created during the rule application
+     * </p>
+     */
+    void addNewlyCreatedParentNode(ITaskTreeNode newParent) {
+        newParents.add(newParent);
+    }
+
+    /**
+     * <p>
+     * return all parent nodes created during the rule application
+     * </p>
+     */
+    List<ITaskTreeNode> getNewlyCreatedParentNodes() {
+        return newParents;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/RuleApplicationStatus.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/RuleApplicationStatus.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/RuleApplicationStatus.java	(revision 922)
@@ -0,0 +1,15 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+/**
+ * <p>
+ * The rule application status describes the result of applying a {@link TemporalRelationshipRule}
+ * on a task tree node. See the description of {@link TemporalRelationshipRule} for more details.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+enum RuleApplicationStatus {
+    RULE_APPLICATION_FINISHED,
+    RULE_APPLICATION_FEASIBLE,
+    RULE_NOT_APPLIED;
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TemporalRelationshipRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TemporalRelationshipRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TemporalRelationshipRule.java	(revision 922)
@@ -0,0 +1,55 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * <p>
+ * a temporal relation ship is able to detected temporal relationships between the child nodes
+ * of the parent node provided to the
+ * {@link #apply(ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory, boolean)} method. A rule
+ * created temporal relationships between the child nodes, i.e. substructures in the task tree, if
+ * it detects a temporal relationship and it can be sure that it is complete. Incomplete but
+ * detected temporal relationships may occur, if there can be more children expected to be added
+ * to the provided parent node. This could be the case during parsing a interaction log file of
+ * a GUI.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+interface TemporalRelationshipRule {
+
+  /**
+   * <p>
+   * applies the rule to the given parent node. The provided builder and node factory are used
+   * to create substructures in the task tree for the identified temporal relationships. The
+   * finalize parameter is used to command the rule to finish rule applications, in the case it
+   * is known that no further data will be available. 
+   * </p>
+   * <p>
+   * The returned rule application result is null, if the rule can not be applied, i.e. it does not
+   * detect a temporal relationship. It returns a rule application result with a status
+   * {@link RuleApplicationStatus#RULE_APPLICATION_FINISHED} if the rule was applied. The result
+   * contains all newly created parent nodes. It returns a rule application result with status
+   * {@link RuleApplicationStatus#RULE_APPLICATION_FEASIBLE} if the rule would be applicable if
+   * further children would be available in the parent node. This status MUST not be returned if 
+   * the finalize parameter is true. In this case the rule must be applied or not. 
+   * </p>
+   * 
+   * @param parent      the parent node with the children to apply the rule on
+   * @param builder     the builder to be used for creating substructures for the identified
+   *                    temporal relationships
+   * @param nodeFactory the node factory to be used for creating substructures for the identified
+   *                    temporal relationships
+   * @param finalize    true, if the rule shall not expect further children to come and that it
+   *                    should therefore be applied in any case
+   *                    
+   * @return the rule application result as described.
+   */
+  RuleApplicationResult apply(ITaskTreeNode        parent,
+                              ITaskTreeBuilder     builder,
+                              ITaskTreeNodeFactory nodeFactory,
+                              boolean              finalize);
+  
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TemporalRelationshipRuleManager.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TemporalRelationshipRuleManager.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TemporalRelationshipRuleManager.java	(revision 922)
@@ -0,0 +1,221 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * This class is responsible for applying temporal relationship rules on a task tree. Through this,
+ * a flat task tree is restructured to have more depth but to include more temporal relationships
+ * between task tree nodes which are not only a major sequence. I.e. through the application of
+ * rule iterations and selections of task tree nodes are detected. Which kind of temporal relations
+ * between task tree nodes are detected depends on the {@link TemporalRelationshipRule}s known to
+ * this class.
+ * </p>
+ * <p>The class holds references to the appropriate {@link TemporalRelationshipRule}s and calls
+ * their {@link TemporalRelationshipRule#apply(ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory, boolean)}
+ * method for each node in the task tree it is needed for. The general behavior of this class is
+ * the following:
+ * <ol>
+ *   <li>
+ *     An instance of this class is created using the constructor and calling the
+ *     {@link #init()} method afterwards
+ *   </li>
+ *   <li>
+ *     then the {@link #applyRules(ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory, boolean)}
+ *     method is called for a so far unstructured task tree node
+ *   </li>
+ *   <li>
+ *     the class iterates its internal list of rules and calls their
+ *     {@link TemporalRelationshipRule#apply(ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory, boolean)}
+ *     method.
+ *   </li>
+ *   <li>
+ *     the class evaluates the rule application result
+ *     <ul>
+ *       <li>
+ *         if a rule returns a rule application result that is null, the next rule is tried
+ *       </li>
+ *       <li>
+ *         if a rule returns that it would be feasible if more data was available and the rule
+ *         application shall not be finalized (see finalize parameter of the applyRules method)
+ *         the rule application is broken up 
+ *       </li>
+ *       <li>
+ *         if a rule returns, that it was applied, the same rule is applied again until it returns
+ *         null or feasible. For each newly created parent node provided in the rule application
+ *         result, the {@link #applyRules(ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory, boolean)}
+ *         method is called.
+ *       </li>
+ *     </ul>
+ *   </li>
+ * </ol>
+ * Through this, all rules are tried to be applied at least once to the provided parent node and
+ * all parent nodes created during the rule application.
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+public class TemporalRelationshipRuleManager {
+    
+    /**
+     * <p>
+     * the node equality manager needed by the rules to compare task tree nodes with each other
+     * </p>
+     */
+    private NodeEqualityRuleManager nodeEqualityRuleManager;
+
+    /**
+     * <p>
+     * the temporal relationship rule known to the manager. The rules are applied in the order
+     * they occur in this list.
+     * </p>
+     */
+    private List<TemporalRelationshipRule> rules = new ArrayList<TemporalRelationshipRule>();
+
+    /**
+     * <p>
+     * initialize the manager with a node equality rule manager to be used by the known rules
+     * for task tree node comparison.
+     * </p>
+     */
+    public TemporalRelationshipRuleManager(NodeEqualityRuleManager nodeEqualityRuleManager) {
+        super();
+        this.nodeEqualityRuleManager = nodeEqualityRuleManager;
+    }
+
+    /**
+     * <p>
+     * initialized the temporal relationship rule manager by instantiating the known rules and
+     * providing them with a reference to the node equality manager or other information they need.
+     * </p>
+     */
+    public void init() {
+        rules.add(new DefaultGuiElementSequenceDetectionRule());
+        rules.add(new DefaultEventTargetSequenceDetectionRule());
+        rules.add(new TrackBarSelectionDetectionRule(nodeEqualityRuleManager));
+        rules.add(new DefaultGuiEventSequenceDetectionRule());
+        
+        rules.add(new DefaultIterationDetectionRule
+                      (nodeEqualityRuleManager, NodeEquality.LEXICALLY_EQUAL));
+        rules.add(new DefaultIterationDetectionRule
+                      (nodeEqualityRuleManager, NodeEquality.SYNTACTICALLY_EQUAL));
+        rules.add(new DefaultIterationDetectionRule
+                      (nodeEqualityRuleManager, NodeEquality.SEMANTICALLY_EQUAL));
+    }
+
+    /**
+     * <p>
+     * applies the known rules to the provided parent node. For the creation of further nodes,
+     * the provided builder and node factory are utilized. If the finalize parameter is true, the
+     * rule application is finalized as far as possible without waiting for further data. If it is
+     * false, the rule application is broken up at the first rule returning, that its application
+     * would be feasible. 
+     * </p>
+     * 
+     * @param parent       the parent node to apply the rules on
+     * @param builder      the task tree builder to be used for linking task tree nodes with each
+     *                     other
+     * @param nodeFactory  the node factory to be used for instantiating new task tree nodes.
+     * @param finalize     used to indicate, if the rule application shall break up if a rule would
+     *                     be feasible if further data was available, or not.
+     */
+    public void applyRules(ITaskTreeNode        parent,
+                           ITaskTreeBuilder     builder,
+                           ITaskTreeNodeFactory nodeFactory,
+                           boolean              finalize)
+    {
+        applyRules(parent, builder, nodeFactory, finalize, "");
+    }
+
+    /**
+     * <p>
+     * applies the known rules to the provided parent node. For the creation of further nodes,
+     * the provided builder and node factory are utilized. If the finalize parameter is true, the
+     * rule application is finalized as far as possible without waiting for further data. If it is
+     * false, the rule application is broken up at the first rule returning, that its application
+     * would be feasible. The method calls itself for each parent node created through the rule
+     * application. In this case, the finalize parameter is always true.
+     * </p>
+     * 
+     * @param parent       the parent node to apply the rules on
+     * @param builder      the task tree builder to be used for linking task tree nodes with each
+     *                     other
+     * @param nodeFactory  the node factory to be used for instantiating new task tree nodes.
+     * @param finalize     used to indicate, if the rule application shall break up if a rule would
+     *                     be feasible if further data was available, or not.
+     * @param logIndent    simply used for loggin purposes to indent the log messages depending
+     *                     on the recursion depth of calling this method.
+     */
+    private int applyRules(ITaskTreeNode        parent,
+                           ITaskTreeBuilder     builder,
+                           ITaskTreeNodeFactory nodeFactory,
+                           boolean              finalize,
+                           String               logIndent)
+    {
+        Console.traceln(Level.FINER, logIndent + "applying rules for " + parent);
+
+        int noOfRuleApplications = 0;
+
+        for (TemporalRelationshipRule rule : rules) {
+            RuleApplicationResult result;
+            do {
+                // LOG.info(logIndent + "trying to apply rule " + rule + " on " + parent);
+                result = rule.apply(parent, builder, nodeFactory, finalize);
+
+                if ((result != null) &&
+                    (result.getRuleApplicationStatus() ==
+                     RuleApplicationStatus.RULE_APPLICATION_FINISHED))
+                {
+                    Console.traceln
+                        (Level.FINE, logIndent + "applied rule " + rule + " on " + parent);
+                    noOfRuleApplications++;
+
+                    for (ITaskTreeNode newParent : result.getNewlyCreatedParentNodes()) {
+                        noOfRuleApplications +=
+                            applyRules(newParent, builder, nodeFactory, true, logIndent + "  ");
+                    }
+                }
+            }
+            while ((result != null) &&
+                   (result.getRuleApplicationStatus() ==
+                    RuleApplicationStatus.RULE_APPLICATION_FINISHED));
+
+            if ((!finalize) &&
+                (result != null) &&
+                (result.getRuleApplicationStatus() ==
+                 RuleApplicationStatus.RULE_APPLICATION_FEASIBLE))
+            {
+                // in this case, don't go on applying rules, which should not be applied yet
+                break;
+            }
+        }
+
+        if (noOfRuleApplications <= 0) {
+            Console.traceln(Level.INFO, logIndent + "no rules applied --> no temporal " +
+                            "relationship generated");
+        }
+
+        return noOfRuleApplications;
+    }
+
+    /**
+     *
+     */
+    /*
+     * private void dumpTask(TaskTreeNode task, String indent) { System.err.print(indent);
+     * System.err.print(task); System.err.println(" ");
+     * 
+     * if ((task.getChildren() != null) && (task.getChildren().size() > 0)) { for (TaskTreeNode
+     * child : task.getChildren()) { dumpTask(child, indent + "  "); } } }
+     */
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TrackBarSelectionDetectionRule.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TrackBarSelectionDetectionRule.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TrackBarSelectionDetectionRule.java	(revision 922)
@@ -0,0 +1,147 @@
+package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
+
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITrackBar;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
+import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 28.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TrackBarSelectionDetectionRule implements TemporalRelationshipRule {
+
+    /** */
+    private NodeEqualityRuleManager nodeEqualityRuleManager;
+
+    /**
+     * TODO: comment
+     * 
+     */
+    TrackBarSelectionDetectionRule(NodeEqualityRuleManager nodeEqualityRuleManager) {
+        super();
+        this.nodeEqualityRuleManager = nodeEqualityRuleManager;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see TemporalRelationshipRule#apply(TaskTreeNode, TaskTreeBuilder, TaskTreeNodeFactory)
+     */
+    @Override
+    public RuleApplicationResult apply(ITaskTreeNode        parent,
+                                       ITaskTreeBuilder     builder,
+                                       ITaskTreeNodeFactory nodeFactory,
+                                       boolean              finalize)
+    {
+        if (!(parent instanceof ISequence)) {
+            return null;
+        }
+
+        RuleApplicationResult result = new RuleApplicationResult();
+
+        int valueSelectionStartIndex = -1;
+
+        int index = 0;
+        while (index < parent.getChildren().size()) {
+            ITaskTreeNode child = parent.getChildren().get(index);
+
+            if ((child instanceof IEventTask) &&
+                (((IEventTask) child).getEventTarget() instanceof ITrackBar) &&
+                (((IEventTask) child).getEventType() instanceof ValueSelection))
+            {
+                if (valueSelectionStartIndex < 0) {
+                    // let the show begin
+                    valueSelectionStartIndex = index;
+                }
+            }
+            else if (valueSelectionStartIndex >= 0) {
+                // current child is no more value selection. But the preceding tasks were.
+                // Therefore,
+                // create an iteration with the different selectable values as selection children
+                handleValueSelections(valueSelectionStartIndex, index - 1, parent, builder,
+                                      nodeFactory, result);
+
+                return result;
+            }
+
+            index++;
+        }
+
+        if (valueSelectionStartIndex >= 0) {
+            if (finalize) {
+                handleValueSelections(valueSelectionStartIndex, parent.getChildren().size() - 1,
+                                      parent, builder, nodeFactory, result);
+            }
+            else {
+                result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param valueSelectionStartIndex
+     * @param i
+     */
+    private void handleValueSelections(int                   startIndex,
+                                       int                   endIndex,
+                                       ITaskTreeNode         parent,
+                                       ITaskTreeBuilder      builder,
+                                       ITaskTreeNodeFactory  nodeFactory,
+                                       RuleApplicationResult result)
+    {
+        IIteration iteration = nodeFactory.createNewIteration();
+        result.addNewlyCreatedParentNode(iteration);
+
+        ISelection selection = nodeFactory.createNewSelection();
+        result.addNewlyCreatedParentNode(selection);
+        builder.setChild(iteration, selection);
+
+        for (int i = endIndex - startIndex; i >= 0; i--) {
+            addChildIfNecessary(selection, parent.getChildren().get(startIndex), builder,
+                                nodeFactory, result);
+            builder.removeChild((ISequence) parent, startIndex);
+        }
+
+        builder.addChild((ISequence) parent, startIndex, iteration);
+
+        result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
+    }
+
+    /**
+     *
+     */
+    private void addChildIfNecessary(ISelection            parentSelection,
+                                     ITaskTreeNode         node,
+                                     ITaskTreeBuilder      builder,
+                                     ITaskTreeNodeFactory  nodeFactory,
+                                     RuleApplicationResult result)
+    {
+        for (int i = 0; i < parentSelection.getChildren().size(); i++) {
+            ITaskTreeNode child = parentSelection.getChildren().get(i);
+
+            // check, if the new node is a variant for the current event task
+            NodeEquality nodeEquality = nodeEqualityRuleManager.applyRules(child, node);
+            if (nodeEquality.isAtLeast(NodeEquality.SYNTACTICALLY_EQUAL)) {
+                return;
+            }
+        }
+
+        // if we did not return in the previous checks, then the node must be added
+        builder.addChild(parentSelection, node);
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/IEventTask.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/IEventTask.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/IEventTask.java	(revision 922)
@@ -0,0 +1,31 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public interface IEventTask extends ITaskTreeNode {
+    
+    /**
+     * @return Returns the event type.
+     */
+    public IEventType getEventType();
+
+    /**
+     * @return Returns the event target.
+     */
+    public IEventTarget getEventTarget();
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public IEventTask clone();
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/IIteration.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/IIteration.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/IIteration.java	(revision 922)
@@ -0,0 +1,18 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface IIteration extends ITemporalRelationship {
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public IIteration clone();
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ISelection.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ISelection.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ISelection.java	(revision 922)
@@ -0,0 +1,18 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface ISelection extends ITemporalRelationship {
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public ISelection clone();
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ISequence.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ISequence.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ISequence.java	(revision 922)
@@ -0,0 +1,18 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface ISequence extends ITemporalRelationship {
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public ISequence clone();
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTree.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTree.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTree.java	(revision 922)
@@ -0,0 +1,31 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+import java.util.Map;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface ITaskTree extends Cloneable {
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public ITaskTreeNode getRoot();
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public Map<ITaskTreeNode, ITaskTreeNodeInfo> getTaskMap();
+
+    /**
+     *
+     */
+    public ITaskTree clone();
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeBuilder.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeBuilder.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeBuilder.java	(revision 922)
@@ -0,0 +1,68 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface ITaskTreeBuilder {
+
+    /**
+     * TODO: comment
+     * 
+     * @param sequence
+     * @param task
+     */
+    void addChild(ISequence parent, ITaskTreeNode child);
+
+    /**
+     * TODO: comment
+     * 
+     * @param parent
+     * @param index
+     * @param sequence
+     */
+    void addChild(ISequence parent, int index, ITaskTreeNode child);
+
+    /**
+     * TODO: comment
+     * 
+     * @param sequence
+     * @param task
+     */
+    void addChild(ISelection parent, ITaskTreeNode child);
+
+    /**
+     * TODO: comment
+     * 
+     * @param iteration
+     * @param newChild
+     */
+    void setChild(IIteration iteration, ITaskTreeNode newChild);
+
+    /**
+     * TODO: comment
+     * 
+     * @param parent
+     * @param i
+     */
+    void removeChild(ISequence parent, int index);
+
+    /**
+     * TODO: comment
+     * 
+     * @param parent
+     * @param i
+     */
+    void removeChild(ISelection parent, ITaskTreeNode child);
+
+    /**
+     * TODO: comment
+     * 
+     * @param parent
+     * @param i
+     */
+    void setDescription(ITaskTreeNode node, String description);
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNode.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNode.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNode.java	(revision 922)
@@ -0,0 +1,43 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+import java.util.List;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public interface ITaskTreeNode extends Cloneable {
+
+    /**
+     *
+     */
+    public String getName();
+
+    /**
+     *
+     */
+    public String getDescription();
+
+    /**
+     *
+     */
+    public List<ITaskTreeNode> getChildren();
+
+    /**
+     *
+     */
+    public boolean equals(ITaskTreeNode taskTreeNode);
+
+    /**
+     *
+     */
+    public int hashCode();
+
+    /**
+     *
+     */
+    public ITaskTreeNode clone();
+    
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNodeFactory.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNodeFactory.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNodeFactory.java	(revision 922)
@@ -0,0 +1,53 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface ITaskTreeNodeFactory
+{
+
+  /**
+   * TODO: comment
+   *
+   * @param eventType
+   * @param eventTarget
+   * @return
+   */
+  IEventTask createNewEventTask(IEventType eventType, IEventTarget eventTarget);
+
+  /**
+   * TODO: comment
+   *
+   * @return
+   */
+  ISequence createNewSequence();
+
+  /**
+   * TODO: comment
+   *
+   * @return
+   */
+  IIteration createNewIteration();
+
+  /**
+   * TODO: comment
+   *
+   * @return
+   */
+  ISelection createNewSelection();
+
+  /**
+   * TODO: comment
+   *
+   * @param rootSequence
+   * @return
+   */
+  ITaskTree createTaskTree(ITaskTreeNode root);
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNodeInfo.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNodeInfo.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITaskTreeNodeInfo.java	(revision 922)
@@ -0,0 +1,26 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface ITaskTreeNodeInfo {
+
+    /**
+     * 
+     */
+    public ITaskTreeNode getTask();
+
+    /**
+     * 
+     */
+    public int getNoOfOccurencesInTree();
+
+    /**
+     *
+     */
+    public long getLastUpdate();
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITemporalRelationship.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITemporalRelationship.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeifc/ITemporalRelationship.java	(revision 922)
@@ -0,0 +1,16 @@
+package de.ugoe.cs.autoquest.tasktrees.treeifc;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 12.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public interface ITemporalRelationship extends ITaskTreeNode {
+
+    /**
+     *
+     */
+    public ITemporalRelationship clone();
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/EventTask.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/EventTask.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/EventTask.java	(revision 922)
@@ -0,0 +1,83 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class EventTask extends TaskTreeNode implements IEventTask {
+    
+    /** */
+    private IEventType eventType;
+
+    /** */
+    private IEventTarget eventTarget;
+
+    /**
+     * @param eventType
+     * @param eventTarget
+     */
+    EventTask(IEventType eventType, IEventTarget eventTarget) {
+        super(eventType.getName() + "(" + eventTarget + ")");
+        super.setDescription(eventType + " on " + eventTarget);
+        this.eventType = eventType;
+        this.eventTarget = eventTarget;
+    }
+
+    /**
+     * @return Returns the interaction.
+     */
+    public IEventType getEventType() {
+        return eventType;
+    }
+
+    /**
+     * @return Returns the GUIElement.
+     */
+    public IEventTarget getEventTarget() {
+        return eventTarget;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.ctte.Task#equals(de.harms.ctte.Task)
+     */
+    @Override
+    public boolean equals(ITaskTreeNode task) {
+        if (!(task instanceof IEventTask)) {
+            return false;
+        }
+
+        IEventType otherType = ((IEventTask) task).getEventType();
+        IEventTarget otherTarget = ((IEventTask) task).getEventTarget();
+
+        if (((eventType == otherType) ||
+             ((eventType != null) && (eventType.equals(otherType)))) &&
+            ((eventTarget == otherTarget) ||
+             ((eventTarget != null) && (eventTarget.equals(otherTarget)))))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.tasktrees.TreeNode#clone()
+     */
+    @Override
+    public EventTask clone() {
+        // Event type and target are unchangeable and do not need to be cloned
+        return (EventTask) super.clone();
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Iteration.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Iteration.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Iteration.java	(revision 922)
@@ -0,0 +1,56 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class Iteration extends TaskTreeNode implements IIteration {
+
+    /**
+     *
+     */
+    Iteration() {
+        super("Iteration");
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeNode#addChild(TaskTreeNode)
+     */
+    @Override
+    public void addChild(ITaskTreeNode child) {
+        // adding more children is not allowed
+        throw new UnsupportedOperationException
+          ("iterations may not have a list of children. Use setChild() instead.");
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param selection
+     * @return
+     */
+    public void setChild(ITaskTreeNode child) {
+        if (super.getChildren().size() > 0) {
+            super.removeChild(0);
+        }
+        super.addChild(child);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#clone()
+     */
+    @Override
+    public Iteration clone() {
+        return (Iteration) super.clone();
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/NodeInfo.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/NodeInfo.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/NodeInfo.java	(revision 922)
@@ -0,0 +1,90 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeInfo;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class NodeInfo implements ITaskTreeNodeInfo {
+    
+    /** */
+    private ITaskTreeNode task;
+
+    /** */
+    private long lastUpdate;
+
+    /** */
+    private List<ITaskTreeNode> parentNodes = new ArrayList<ITaskTreeNode>();
+
+    /**
+     * @param node
+     */
+    NodeInfo(ITaskTreeNode task) {
+        this.task = task;
+        lastUpdate = System.currentTimeMillis();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.NodeInfo#getTask()
+     */
+    @Override
+    public ITaskTreeNode getTask() {
+        return task;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeimpl.NodeInfo#getNoOfOccurencesInTree()
+     */
+    @Override
+    public int getNoOfOccurencesInTree() {
+        return parentNodes.size();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeimpl.NodeInfo#getLastUpdate()
+     */
+    @Override
+    public long getLastUpdate() {
+        return lastUpdate;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    void addParent(ITaskTreeNode parent) {
+        parentNodes.add(parent);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    void removeParent(ITaskTreeNode parent) {
+        parentNodes.remove(parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public synchronized String toString() {
+        return "NodeInfo(" + task + ", " + parentNodes.size() + " parents)";
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Selection.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Selection.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Selection.java	(revision 922)
@@ -0,0 +1,32 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class Selection extends TaskTreeNode implements ISelection {
+
+    /**
+     * TODO: comment
+     * 
+     * @param name
+     */
+    Selection() {
+        super("Selection");
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#clone()
+     */
+    @Override
+    public Selection clone() {
+        return (Selection) super.clone();
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Sequence.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Sequence.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/Sequence.java	(revision 922)
@@ -0,0 +1,41 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 19.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class Sequence extends TaskTreeNode implements ISequence {
+    
+    /**
+     * TODO: comment
+     * 
+     * @param name
+     */
+    Sequence() {
+        super("Sequence");
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param name
+     */
+    Sequence(String name) {
+        super(name);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeimpl.TaskTreeNodeImpl#clone()
+     */
+    @Override
+    public Sequence clone() {
+        return (Sequence) super.clone();
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTree.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTree.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTree.java	(revision 922)
@@ -0,0 +1,107 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeInfo;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TaskTree implements ITaskTree {
+    
+    /** the map of nodes */
+    private Map<ITaskTreeNode, ITaskTreeNodeInfo> taskMap;
+
+    /** the root node of the task tree */
+    private ITaskTreeNode rootNode;
+
+    /**
+     * TODO: comment
+     * 
+     */
+    TaskTree(ITaskTreeNode rootNode) {
+        this.rootNode = rootNode;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTree#getRoot()
+     */
+    @Override
+    public ITaskTreeNode getRoot() {
+        return rootNode;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTree#getTaskMap()
+     */
+    @Override
+    public synchronized Map<ITaskTreeNode, ITaskTreeNodeInfo> getTaskMap() {
+        if (taskMap == null) {
+            taskMap = new HashMap<ITaskTreeNode, ITaskTreeNodeInfo>();
+            addNodeToMap(rootNode, null);
+        }
+        return taskMap;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param rootNode
+     */
+    private void addNodeToMap(ITaskTreeNode node, ITaskTreeNode parent) {
+        NodeInfo nodeInfo = (NodeInfo) taskMap.get(node);
+
+        if (nodeInfo == null) {
+            nodeInfo = new NodeInfo(node);
+            taskMap.put(node, nodeInfo);
+        }
+
+        if (parent != null) {
+            // through first removing an existing parent it is assured, that a parent is recorded
+            // only once. This is needed, because parent may be reused in a tree as well, but we
+            // always iterate the whole tree
+            nodeInfo.removeParent(parent);
+            nodeInfo.addParent(parent);
+        }
+
+        for (ITaskTreeNode child : node.getChildren()) {
+            addNodeToMap(child, node);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#clone()
+     */
+    @Override
+    public TaskTree clone() {
+        TaskTree clone = null;
+        try {
+            clone = (TaskTree) super.clone();
+
+            clone.rootNode = rootNode.clone();
+
+            // the clone will create the task map itself, when it is first retrieved
+            clone.taskMap = null;
+
+        }
+        catch (CloneNotSupportedException e) {
+            // this should never happen. Therefore simply dump the exception
+            e.printStackTrace();
+        }
+
+        return clone;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeBuilder.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeBuilder.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeBuilder.java	(revision 922)
@@ -0,0 +1,152 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TaskTreeBuilder implements ITaskTreeBuilder {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeBuilder#addChild(Sequence, TaskTreeNode)
+     */
+    @Override
+    public void addChild(ISequence parent, ITaskTreeNode child) {
+        if (!(parent instanceof Sequence)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + parent.getClass());
+        }
+
+        addChildInternal(parent, -1, child);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeBuilder#addChild(Sequence, int, TaskTreeNode)
+     */
+    @Override
+    public void addChild(ISequence parent, int index, ITaskTreeNode child) {
+        if (!(parent instanceof Sequence)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + parent.getClass());
+        }
+
+        addChildInternal(parent, index, child);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeBuilder#addChild(Selection, TaskTreeNode)
+     */
+    @Override
+    public void addChild(ISelection parent, ITaskTreeNode child) {
+        if (!(parent instanceof Selection)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + parent.getClass());
+        }
+
+        addChildInternal(parent, -1, child);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeBuilder#setChild(Iteration, TaskTreeNode)
+     */
+    @Override
+    public void setChild(IIteration iteration, ITaskTreeNode newChild) {
+        if (!(iteration instanceof Iteration)) {
+            throw new IllegalArgumentException
+                ("illegal type of iteration provided: " + iteration.getClass());
+        }
+
+        if (!(newChild instanceof TaskTreeNode)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + newChild.getClass());
+        }
+
+        ((Iteration) iteration).setChild(newChild);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeBuilder#removeChild(Sequence, int)
+     */
+    @Override
+    public void removeChild(ISequence parent, int index) {
+        if (!(parent instanceof TaskTreeNode)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + parent.getClass());
+        }
+
+        ((TaskTreeNode) parent).removeChild(index);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeBuilder#removeChild(Selection, TaskTreeNode)
+     */
+    @Override
+    public void removeChild(ISelection parent, ITaskTreeNode child) {
+        if (!(parent instanceof TaskTreeNode)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + parent.getClass());
+        }
+
+        for (int i = 0; i < parent.getChildren().size(); i++) {
+            if ((parent.getChildren().get(i) == child) ||
+                ((parent.getChildren().get(i) != null) &&
+                 (parent.getChildren().get(i).equals(child))))
+            {
+                ((TaskTreeNode) parent).removeChild(i);
+                break;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeBuilder#setDescription(TaskTreeNode, String)
+     */
+    @Override
+    public void setDescription(ITaskTreeNode parent, String description) {
+        if (!(parent instanceof TaskTreeNode)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + parent.getClass());
+        }
+
+        ((TaskTreeNode) parent).setDescription(description);
+    }
+
+    /**
+   * 
+   */
+    private void addChildInternal(ITaskTreeNode parent, int index, ITaskTreeNode child) {
+        if (!(child instanceof TaskTreeNode)) {
+            throw new IllegalArgumentException
+                ("illegal type of task tree node provided: " + child.getClass());
+        }
+
+        if (index > -1) {
+            ((TaskTreeNode) parent).addChild(index, child);
+        }
+        else {
+            ((TaskTreeNode) parent).addChild(child);
+        }
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeNode.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeNode.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeNode.java	(revision 922)
@@ -0,0 +1,220 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class TaskTreeNode implements ITaskTreeNode {
+    /** */
+    private static int temporalId = 0;
+
+    /** */
+    private String name;
+
+    /** */
+    private String description;
+
+    /** */
+    private int id;
+
+    /** children */
+    private List<ITaskTreeNode> children;
+
+    /**
+     * 
+     */
+    public TaskTreeNode(String name) {
+        this.name = name;
+        id = getNewId();
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    private static synchronized int getNewId() {
+        if (temporalId == Integer.MAX_VALUE) {
+            temporalId = 0;
+        }
+
+        return temporalId++;
+    }
+
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeNode#getDescription()
+     */
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * 
+     */
+    public synchronized List<ITaskTreeNode> getChildren() {
+        if ((children == null) || (children.size() == 0)) {
+            return new ArrayList<ITaskTreeNode>();
+        }
+
+        return children.subList(0, children.size());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeNode#equals(TaskTreeNode)
+     */
+    @Override
+    public boolean equals(ITaskTreeNode taskTreeNode) {
+        if (!this.getClass().isInstance(taskTreeNode)) {
+            return false;
+        }
+
+        if (taskTreeNode.hashCode() != hashCode()) {
+            return false;
+        }
+
+        TaskTreeNode other = (TaskTreeNode) taskTreeNode;
+
+        if (id != other.id) {
+            return false;
+        }
+
+        if (!name.equals(other.name)) {
+            return false;
+        }
+
+        synchronized (other) {
+            if (children == null) {
+                return (other.children == null);
+            }
+            else if (other.children == null) {
+                return (children == null);
+            }
+            else if (other.children.size() != children.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < children.size(); i++) {
+                if (!children.get(i).equals(other.children.get(i))) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public synchronized int hashCode() {
+        return getClass().getSimpleName().hashCode();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public synchronized String toString() {
+        if (children == null) {
+            return name + "(" + id + ")";
+        }
+        else {
+            return name + "(" + id + ", " + children.size() + " children)";
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param i
+     * @return
+     */
+    void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * 
+     */
+    synchronized void addChild(ITaskTreeNode child) {
+        if (children == null) {
+            children = new ArrayList<ITaskTreeNode>();
+        }
+
+        children.add(child);
+    }
+
+    /**
+     * 
+     */
+    synchronized void addChild(int index, ITaskTreeNode child) {
+        if (children == null) {
+            children = new ArrayList<ITaskTreeNode>();
+        }
+
+        children.add(index, child);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param i
+     * @return
+     */
+    synchronized ITaskTreeNode removeChild(int index) {
+        return children.remove(index);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#clone()
+     */
+    @Override
+    public ITaskTreeNode clone() {
+        TaskTreeNode clone = null;
+        try {
+            clone = (TaskTreeNode) super.clone();
+
+            if (children != null) {
+                clone.children = new ArrayList<ITaskTreeNode>();
+
+                for (ITaskTreeNode child : children) {
+                    clone.children.add(child.clone());
+                }
+            }
+
+        }
+        catch (CloneNotSupportedException e) {
+            // this should never happen. Therefore simply dump the exception
+            e.printStackTrace();
+        }
+
+        return clone;
+    }
+
+}
Index: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeNodeFactory.java
===================================================================
--- trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeNodeFactory.java	(revision 922)
+++ trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/treeimpl/TaskTreeNodeFactory.java	(revision 922)
@@ -0,0 +1,73 @@
+package de.ugoe.cs.autoquest.tasktrees.treeimpl;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 21.02.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TaskTreeNodeFactory implements ITaskTreeNodeFactory {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see TaskTreeNodeFactory#createNewEventTask(IEventType, IEventTarget)
+     */
+    @Override
+    public IEventTask createNewEventTask(IEventType eventType, IEventTarget eventTarget) {
+        return new EventTask(eventType, eventTarget);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeNodeFactory#createNewSequence()
+     */
+    @Override
+    public ISequence createNewSequence() {
+        return new Sequence();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeNodeFactory#createNewIteration()
+     */
+    @Override
+    public IIteration createNewIteration() {
+        return new Iteration();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.tasktree.treeifc.TaskTreeNodeFactory#createNewSelection()
+     */
+    @Override
+    public ISelection createNewSelection() {
+        return new Selection();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * de.ugoe.cs.tasktree.treeifc.TaskTreeNodeFactory#createTaskTree(de.ugoe.cs.tasktree.treeifc
+     * .TaskTreeNode)
+     */
+    @Override
+    public ITaskTree createTaskTree(ITaskTreeNode root) {
+        return new TaskTree(root);
+    }
+
+}
Index: trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/DrawFromAllSequencesGenerator.java
===================================================================
--- trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/DrawFromAllSequencesGenerator.java	(revision 922)
+++ trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/DrawFromAllSequencesGenerator.java	(revision 922)
@@ -0,0 +1,180 @@
+package de.ugoe.cs.autoquest.testgeneration;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Generates a test suite by drawing from all possible sequences of a fixed length according to the
+ * probabilities of the sequences in a {@link IStochasticProcess}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class DrawFromAllSequencesGenerator {
+
+    /**
+     * <p>
+     * Number of sequences in the test suite.
+     * </p>
+     */
+    private final int numSequences;
+
+    /**
+     * <p>
+     * Minimal length of a test sequence.
+     * </p>
+     */
+    private final int minLength;
+
+    /**
+     * <p>
+     * Maximal length of a test sequence.
+     * </p>
+     */
+    private final int maxLength;
+
+    /**
+     * <p>
+     * In case this member is true, only test cases that end in the global end event
+     * {@link Event#ENDEVENT} are generated. If it is false, the end event can be any event.
+     * </p>
+     */
+    private final boolean validEnd;
+
+    /**
+     * <p>
+     * If this member is true, the generated test suite contains all possible sequences and
+     * {@link #numSequences} is ignored.
+     * </p>
+     */
+    private final boolean generateAll;
+
+    /**
+     * <p>
+     * Constructor. Creates a new DrawFromAllSequencesGenerator and ensures the validity of the
+     * parameters:
+     * <ul>
+     * <li>numSequences must at least be 1
+     * <li>maxLength must at least be 1
+     * <li>minLength must be less than or equal to maxLength
+     * </ul>
+     * If one of these conditions is violated an {@link IllegalArgumentException} is thrown.
+     * </p>
+     * 
+     * @param numSequences
+     *            number of sequences desired for the test suite
+     * @param minLength
+     *            minimal length of a test sequence
+     * @param maxLength
+     *            maximal length of a test sequence
+     * @param validEnd
+     *            defines if test cases have to end with the global end event {@link Event#ENDEVENT}
+     *            (see {@link #validEnd})
+     * @param generateAll
+     *            if this parameter is true, the test suite contains all possible sequences and
+     *            numSequences is ignored
+     */
+    public DrawFromAllSequencesGenerator(int numSequences,
+                                         int minLength,
+                                         int maxLength,
+                                         boolean validEnd,
+                                         boolean generateAll)
+    {
+        // check validity of the parameters
+        if (numSequences < 1) {
+            throw new IllegalArgumentException("number of sequences must be at least 1 but is " +
+                numSequences);
+        }
+        if (maxLength < 1) {
+            throw new IllegalArgumentException(
+                                                "maximal allowed length of test cases must be at least 1 but is " +
+                                                    maxLength);
+        }
+        if (minLength > maxLength) {
+            throw new IllegalArgumentException(
+                                                "minimal allowed length of test cases must be less than or equal to the maximal allowed length (min length: " +
+                                                    minLength + " ; max length: " + maxLength + ")");
+        }
+        this.numSequences = numSequences;
+        this.minLength = minLength;
+        this.maxLength = maxLength;
+        this.validEnd = validEnd;
+        this.generateAll = generateAll;
+    }
+
+    /**
+     * <p>
+     * Generates a test suite by drawing from all possible sequences with valid lengths.
+     * </p>
+     * 
+     * @param model
+     *            model used to determine the probability of each possible sequence
+     * @return the test suite
+     */
+    public Collection<List<Event>> generateTestSuite(IStochasticProcess model) {
+        if (model == null) {
+            throw new IllegalArgumentException("model must not be null!");
+        }
+
+        Collection<List<Event>> sequences = new LinkedHashSet<List<Event>>();
+        for (int length = minLength; length <= maxLength; length++) {
+            if (validEnd) {
+                sequences.addAll(model.generateValidSequences(length + 2));
+            }
+            else {
+                sequences.addAll(model.generateSequences(length + 1, true));
+            }
+        }
+        Console.traceln(Level.INFO, "" + sequences.size() + " possible");
+        if (!generateAll && numSequences < sequences.size()) {
+            List<Double> probabilities = new ArrayList<Double>(sequences.size());
+            double probSum = 0.0;
+            for (List<Event> sequence : sequences) {
+                double prob = model.getProbability(sequence);
+                probabilities.add(prob);
+                probSum += prob;
+            }
+            Set<Integer> drawnSequences = new HashSet<Integer>(numSequences);
+            Random r = new Random();
+            while (drawnSequences.size() < numSequences) {
+                double randVal = r.nextDouble() * probSum;
+                double sum = 0.0d;
+                int index = -1;
+                while (sum < randVal) {
+                    index++;
+                    double currentProb = probabilities.get(index);
+                    sum += currentProb;
+                }
+                if (!drawnSequences.contains(index)) {
+                    drawnSequences.add(index);
+                    probSum -= probabilities.get(index);
+                    probabilities.set(index, 0.0d);
+                }
+            }
+            Collection<List<Event>> retainedSequences = new LinkedList<List<Event>>();
+            int index = 0;
+            for (List<Event> sequence : sequences) {
+                if (drawnSequences.contains(index)) {
+                    retainedSequences.add(sequence);
+                }
+                index++;
+            }
+            sequences = retainedSequences;
+        }
+        return sequences;
+    }
+
+}
Index: trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/HybridGenerator.java
===================================================================
--- trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/HybridGenerator.java	(revision 922)
+++ trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/HybridGenerator.java	(revision 922)
@@ -0,0 +1,212 @@
+package de.ugoe.cs.autoquest.testgeneration;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Generates a test suite with a hybrid approach that is a mixture of random walks and drawing from
+ * all possible sequences.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class HybridGenerator {
+
+    /**
+     * <p>
+     * Number of sequences in the test suite.
+     * </p>
+     */
+    private final int numSequences;
+
+    /**
+     * <p>
+     * Length of a test sequence.
+     * </p>
+     */
+    private final int length;
+
+    /**
+     * <p>
+     * Maximal length where it is possible to generate all sequences and draw from them.
+     * </p>
+     */
+    private final int maxLengthAll;
+
+    /**
+     * <p>
+     * In case this member is true, only test cases that end in the global end event
+     * {@link Event#ENDEVENT} are generated. If it is false, the end event can be any event.
+     * </p>
+     */
+    private final boolean validEnd;
+
+    /**
+     * <p>
+     * Constructor. Creates a new HybridGenerator and ensures the validity of the parameters:
+     * <ul>
+     * <li>numSequences must at least be 1
+     * <li>length must be at least 1
+     * </ul>
+     * If one of these conditions is violated an {@link IllegalArgumentException} is thrown.
+     * </p>
+     * 
+     * @param numSequences
+     *            number of sequences desired for the test suite
+     * @param length
+     *            length of a test sequence
+     * @param maxLengthAll
+     *            maximal length where it is possible to generate all sequences and draw from them
+     * @param validEnd
+     *            defines if test cases have to end with the global end event {@link Event#ENDEVENT}
+     *            (see {@link #validEnd})
+     */
+    public HybridGenerator(int numSequences, int length, int maxLengthAll, boolean validEnd) {
+        // check validity of the parameters
+        if (numSequences < 1) {
+            throw new IllegalArgumentException("number of sequences must be at least 1 but is " +
+                numSequences);
+        }
+        if (length < 1) {
+            throw new IllegalArgumentException("length of test cases must be at least 1 but is " +
+                length);
+        }
+        this.numSequences = numSequences;
+        this.length = length;
+        this.maxLengthAll = maxLengthAll;
+        this.validEnd = validEnd;
+    }
+
+    /**
+     * <p>
+     * Generates a test suite with a hybrid approach that is a mixture of random walks and drawing
+     * from all possible sequences
+     * </p>
+     * 
+     * @param model
+     *            model used to determine the probability of each possible sequence
+     * @return the test suite
+     */
+    public Collection<List<Event>> generateTestSuite(IStochasticProcess model) {
+        if (model == null) {
+            throw new IllegalArgumentException("model must not be null!");
+        }
+
+        Collection<List<Event>> sequences = new LinkedHashSet<List<Event>>();
+
+        List<List<Event>> seqsTmp =
+            new ArrayList<List<Event>>(model.generateSequences(maxLengthAll + 1, true));
+
+        Console.traceln(Level.INFO, "" + seqsTmp.size() + " of length " + maxLengthAll + " possible");
+        List<Double> probabilities = new ArrayList<Double>(seqsTmp.size());
+        double probSum = 0.0;
+        for (List<Event> sequence : seqsTmp) {
+            double prob = model.getProbability(sequence);
+            probabilities.add(prob);
+            probSum += prob;
+        }
+
+        Random r = new Random();
+        int j = 0;
+        while (sequences.size() < numSequences && j <= numSequences * 100) {
+            j++;
+            double randVal = r.nextDouble() * probSum;
+            double sum = 0.0d;
+            int index = -1;
+            while (sum < randVal) {
+                index++;
+                double currentProb = probabilities.get(index);
+                sum += currentProb;
+            }
+            List<Event> seqTmp = seqsTmp.get(index);
+            if (!Event.ENDEVENT.equals(seqTmp.get(seqTmp.size() - 1))) {
+                List<Event> sequence;
+                if (validEnd) {
+                    sequence = finishSequence(seqTmp, model, length + 2, validEnd);
+                    if (sequence != null && sequence.size() != length + 2) {
+                        sequence = null;
+                    }
+                }
+                else {
+                    sequence = finishSequence(seqTmp, model, length + 1, validEnd);
+                    if (sequence != null && sequence.size() != length + 1) {
+                        sequence = null;
+                    }
+                }
+                if (sequence != null) {
+                    sequences.add(sequence);
+                }
+            }
+        }
+
+        return sequences;
+    }
+
+    /**
+     * <p>
+     * Finishes a sequence with a random walk.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence to be finished
+     * @param model
+     *            model used for the random walk
+     * @param length
+     *            desired length of the sequence
+     * @param validEnd
+     *            if the sequence should end in {@link Event#ENDEVENT}.
+     * @return finished sequence of the desired length
+     */
+    private List<Event> finishSequence(List<Event> sequence,
+                                       IStochasticProcess model,
+                                       int length,
+                                       boolean validEnd)
+    {
+        Random r = new Random();
+        boolean endFound = false;
+        List<Event> sequenceCopy = new LinkedList<Event>(sequence);
+        final int maxIter = 30000;
+        int iter = 0;
+        while (!endFound && iter < maxIter) {
+            iter++;
+            sequenceCopy = new LinkedList<Event>(sequence);
+            while (!endFound && sequenceCopy.size() <= length) {
+                double randVal = r.nextDouble();
+                double probSum = 0.0;
+                for (Event symbol : model.getEvents()) {
+                    probSum += model.getProbability(sequenceCopy, symbol);
+                    if (probSum >= randVal) {
+                        if (!(Event.STARTEVENT.equals(symbol) || (!validEnd && Event.ENDEVENT
+                            .equals(symbol))))
+                        {
+                            // only add the symbol the sequence if it is not
+                            // START
+                            // or END
+                            sequenceCopy.add(symbol);
+                        }
+                        endFound =
+                            Event.ENDEVENT.equals(symbol) ||
+                                (!validEnd && sequenceCopy.size() == length);
+                        break;
+                    }
+                }
+            }
+        }
+        if (iter == maxIter) {
+            return null;
+        }
+        return sequenceCopy;
+    }
+
+}
Index: trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/RandomWalkGenerator.java
===================================================================
--- trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/RandomWalkGenerator.java	(revision 922)
+++ trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/RandomWalkGenerator.java	(revision 922)
@@ -0,0 +1,172 @@
+package de.ugoe.cs.autoquest.testgeneration;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+
+/**
+ * <p>
+ * Generates a test suite by randomly walking an {@link IStochasticProcess}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class RandomWalkGenerator {
+
+    /**
+     * <p>
+     * Number of sequences in the test suite.
+     * </p>
+     */
+    private final int numSequences;
+
+    /**
+     * <p>
+     * Minimal length of a test sequence.
+     * </p>
+     */
+    private final int minLength;
+
+    /**
+     * <p>
+     * Maximal length of a test sequence.
+     * </p>
+     */
+    private final int maxLength;
+
+    /**
+     * <p>
+     * In case this member is true, only test cases that end in the global end event
+     * {@link Event#ENDEVENT} are generated. If it is false, the end event can be any event.
+     * </p>
+     */
+    private final boolean validEnd;
+
+    /**
+     * <p>
+     * Maximal number of random walks performed before aborting the test case generation and
+     * returning a test suite with less than {@link #numSequences} test cases. This can happen if
+     * too many generated random walks have to be discarded because their length is not between
+     * {@link #minLength} and {@link #maxLength}.
+     * </p>
+     */
+    private final long maxIter;
+
+    /**
+     * <p>
+     * Actual number of random walks performed to generate the test suite.
+     * </p>
+     */
+    private long actualIter = -1;
+
+    /**
+     * <p>
+     * Constructor. Creates a new RandomWalkGenerator and ensures the validity of the parameters:
+     * <ul>
+     * <li>numSequences must at least be 1
+     * <li>maxLength must at least be 1
+     * <li>minLength must be less than or equal to maxLength
+     * <li>maxIter must be greater than or equal to numSequences
+     * </ul>
+     * If one of these conditions is violated an {@link IllegalArgumentException} is thrown.
+     * </p>
+     * 
+     * @param numSequences
+     *            number of sequences desired for the test suite
+     * @param minLength
+     *            minimal length of a test sequence
+     * @param maxLength
+     *            maximal length of a test sequence
+     * @param validEnd
+     *            defines if test cases have to end with the global end event {@link Event#ENDEVENT}
+     *            (see {@link #validEnd})
+     * @param maxIter
+     *            maximal number of random walks before aborting the test case generation (see
+     *            {@link #maxIter})
+     */
+    public RandomWalkGenerator(int numSequences,
+                               int minLength,
+                               int maxLength,
+                               boolean validEnd,
+                               long maxIter)
+    {
+        // check validity of the parameters
+        if (numSequences < 1) {
+            throw new IllegalArgumentException("number of sequences must be at least 1 but is " +
+                numSequences);
+        }
+        if (maxLength < 1) {
+            throw new IllegalArgumentException(
+                                                "maximal allowed length of test cases must be at least 1 but is " +
+                                                    maxLength);
+        }
+        if (minLength > maxLength) {
+            throw new IllegalArgumentException(
+                                                "minimal allowed length of test cases must be less than or equal to the maximal allowed length (min length: " +
+                                                    minLength + " ; max length: " + maxLength + ")");
+        }
+        if (maxIter < numSequences) {
+            throw new IllegalArgumentException(
+                                                "maximal number of iterations must greater than or equal to the number of sequences (number of sequences: " +
+                                                    numSequences +
+                                                    " ; max iterations: " +
+                                                    maxIter +
+                                                    ")");
+        }
+        this.numSequences = numSequences;
+        this.minLength = minLength;
+        this.maxLength = maxLength;
+        this.validEnd = validEnd;
+        this.maxIter = maxIter;
+    }
+
+    /**
+     * <p>
+     * Generates a test suite by repeatedly randomly walking a stochastic process.
+     * </p>
+     * 
+     * @param model
+     *            stochastic process which performs the random walks
+     * @return the test suite
+     */
+    public Collection<List<Event>> generateTestSuite(IStochasticProcess model) {
+        if (model == null) {
+            throw new IllegalArgumentException("model must not be null!");
+        }
+
+        Set<List<Event>> sequences = new HashSet<List<Event>>(numSequences);
+        actualIter = 0;
+        while (sequences.size() < numSequences && actualIter < maxIter) {
+            List<Event> generatedSequence = model.randomSequence(maxLength, validEnd);
+            if (generatedSequence.size() >= minLength && generatedSequence.size() <= maxLength) {
+                ((List<Event>) generatedSequence).add(0, Event.STARTEVENT);
+                if (validEnd) {
+                    ((List<Event>) generatedSequence).add(Event.ENDEVENT);
+                }
+                sequences.add(generatedSequence);
+            }
+            actualIter++;
+        }
+
+        return sequences;
+    }
+
+    /**
+     * <p>
+     * Returns the actual number of random walks performed during the last call of
+     * {@link #generateTestSuite(IStochasticProcess)} or -1 if
+     * {@link #generateTestSuite(IStochasticProcess)} has not been called yet.
+     * </p>
+     * 
+     * @return actual number of random walks or -1 if {@link #generateTestSuite(IStochasticProcess)}
+     *         has not been called
+     */
+    public long getActualIter() {
+        return actualIter;
+    }
+}
Index: trunk/autoquest-core-usability-test/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-core-usability-test/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-core-usability-test/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/AbstractUsabilityEvaluationTC.java
===================================================================
--- trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/AbstractUsabilityEvaluationTC.java	(revision 922)
+++ trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/AbstractUsabilityEvaluationTC.java	(revision 922)
@@ -0,0 +1,212 @@
+package de.ugoe.cs.autoquest.usability;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.Before;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeBuilder;
+import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskTreeNodeFactory;
+import de.ugoe.cs.autoquest.test.DummyGUIElement;
+import de.ugoe.cs.autoquest.test.DummyInteraction;
+import de.ugoe.cs.autoquest.test.DummyTextField;
+import de.ugoe.cs.autoquest.usability.UsabilityDefect;
+import de.ugoe.cs.autoquest.usability.UsabilityEvaluationResult;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class AbstractUsabilityEvaluationTC {
+
+    /** */
+    private ITaskTreeBuilder taskTreeBuilder = new TaskTreeBuilder();
+
+    /** */
+    private ITaskTreeNodeFactory taskTreeNodeFactory = new TaskTreeNodeFactory();
+
+    /**
+   *
+   */
+    @Before
+    public void setUp() {
+        new TextConsole(Level.FINEST);
+    }
+
+    /**
+   *
+   */
+    protected ITaskTree createTaskTree(String spec) {
+        Matcher matcher =
+            Pattern.compile("(\\s*(\\w+)\\s*(\\(((\\w*\\s*)*)\\))?\\s*(\\{))|(\\})").matcher(spec);
+
+        if (!matcher.find()) {
+            if (!matcher.hitEnd()) {
+                throw new IllegalArgumentException("could not parse task specification");
+            }
+        }
+
+        return taskTreeNodeFactory.createTaskTree(parseTask(matcher, new int[1]));
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param expectedDefects
+     * @param evaluateUsability
+     */
+    protected void assertUsabilityEvaluationResult(UsabilityDefect[]         expectedDefects,
+                                                   UsabilityEvaluationResult evaluationResult)
+    {
+        assertEquals(expectedDefects.length, evaluationResult.getAllDefects().size());
+
+        EXPECTED_DEFECT_ITERATION:
+        for (UsabilityDefect expectedDefect : expectedDefects) {
+            for (UsabilityDefect defect : evaluationResult.getAllDefects()) {
+                if (expectedDefect.equals(defect)) {
+                    System.err.println(defect.getParameterizedDescription());
+                    continue EXPECTED_DEFECT_ITERATION;
+                }
+            }
+
+            for (UsabilityDefect defect : evaluationResult.getAllDefects()) {
+                System.err.println(defect);
+            }
+
+            fail("expected defect " + expectedDefect + " not found in evaluation result");
+        }
+    }
+
+    /**
+   * 
+   */
+    private ITaskTreeNode parseTask(Matcher tokenMatcher, int[] typeNumbers) {
+        String firstToken = tokenMatcher.group();
+
+        if ("}".equals(firstToken)) {
+            throw new IllegalArgumentException("found a closing bracket at an unexpected place");
+        }
+
+        ITaskTreeNode treeNode = instantiateTreeNode(tokenMatcher, typeNumbers);
+
+        if (!tokenMatcher.find()) {
+            throw new IllegalArgumentException("could not parse task specification");
+        }
+
+        firstToken = tokenMatcher.group();
+
+        if (!"}".equals(firstToken)) {
+            ITaskTreeNode child = null;
+
+            do {
+                child = parseTask(tokenMatcher, typeNumbers);
+
+                if (child != null) {
+                    addChild(treeNode, child);
+
+                    if (!tokenMatcher.find()) {
+                        throw new IllegalArgumentException("could not parse task specification");
+                    }
+
+                    firstToken = tokenMatcher.group();
+
+                    if ("}".equals(firstToken)) {
+                        break;
+                    }
+                }
+
+            }
+            while (child != null);
+
+        }
+
+        return treeNode;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param group
+     * @return
+     */
+    private ITaskTreeNode instantiateTreeNode(Matcher tokenMatcher, int[] typeNumbers) {
+        String type = tokenMatcher.group(2);
+        String additionalInfo = tokenMatcher.group(4);
+
+        if ("Interaction".equals(type)) {
+            return taskTreeNodeFactory.createNewEventTask
+                (new DummyInteraction("dummy", typeNumbers[0]++), new DummyGUIElement("dummy"));
+        }
+        else if ("Sequence".equals(type)) {
+            return taskTreeNodeFactory.createNewSequence();
+        }
+        else if ("Iteration".equals(type)) {
+            return taskTreeNodeFactory.createNewIteration();
+        }
+        else if ("Selection".equals(type)) {
+            return taskTreeNodeFactory.createNewSelection();
+        }
+        else if ("TextInput".equals(type)) {
+            if (additionalInfo == null) {
+                fail("no simulated text provided for text input interactin task");
+            }
+
+            TextInput textInput = new TextInput(additionalInfo, new ArrayList<Event>());
+            
+            IEventTask task = taskTreeNodeFactory.createNewEventTask
+                (textInput, new DummyTextField(additionalInfo));
+
+            return task;
+        }
+        else {
+            fail("invalid type of task tree node: " + type);
+            return null;
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param treeNode
+     * @param child
+     */
+    private void addChild(ITaskTreeNode parent, ITaskTreeNode child) {
+        if (parent instanceof ISequence) {
+            taskTreeBuilder.addChild((ISequence) parent, child);
+        }
+        else if (parent instanceof IIteration) {
+            if (parent.getChildren().size() <= 0) {
+                taskTreeBuilder.setChild((IIteration) parent, child);
+            }
+            else {
+                fail("can not add more than one child to an iteration");
+            }
+        }
+        else if (parent instanceof ISelection) {
+            taskTreeBuilder.addChild((ISelection) parent, child);
+        }
+        else {
+            fail("can not add children to parent task tree node of type " +
+                 parent.getClass().getName());
+        }
+    }
+
+}
Index: trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRuleTest.java
===================================================================
--- trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRuleTest.java	(revision 922)
+++ trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRuleTest.java	(revision 922)
@@ -0,0 +1,680 @@
+package de.ugoe.cs.autoquest.usability;
+
+import static de.ugoe.cs.autoquest.usability.UsabilityDefectDescription.TEXT_FIELD_INPUT_RATIO;
+import static de.ugoe.cs.autoquest.usability.UsabilityDefectDescription.TEXT_FIELD_INPUT_REPETITIONS;
+import static de.ugoe.cs.autoquest.usability.UsabilityDefectDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO;
+import static de.ugoe.cs.autoquest.usability.UsabilityDefectSeverity.HIGH;
+import static de.ugoe.cs.autoquest.usability.UsabilityDefectSeverity.INFO;
+import static de.ugoe.cs.autoquest.usability.UsabilityDefectSeverity.LOW;
+import static de.ugoe.cs.autoquest.usability.UsabilityDefectSeverity.MEDIUM;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.usability.UsabilityDefect;
+import de.ugoe.cs.autoquest.usability.UsabilityDefectDescription;
+import de.ugoe.cs.autoquest.usability.UsabilityEvaluationManager;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class TextInputStatisticsRuleTest extends AbstractUsabilityEvaluationTC {
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testWithDifferentTextInputInteractionsOnly() {
+        UsabilityEvaluationManager manager = new UsabilityEvaluationManager();
+
+        // ===== check =====
+        String spec =
+            "TextInput (bla) {}";
+        UsabilityDefect[] expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (bla) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, UsabilityDefectDescription.TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a) {}" +
+            "  TextInput (b) {}" +
+            "  TextInput (c) {}" +
+            "  TextInput (d) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Selection {" +
+            "  TextInput (a) {}" +
+            "  TextInput (b) {}" +
+            "  TextInput (c) {}" +
+            "  TextInput (d) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Iteration {" +
+            "  TextInput (bla) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a) {}" +
+            "  Sequence {" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    TextInput (d) {}" +
+            "    TextInput (e) {}" +
+            "  }" +
+            "  Iteration {" +
+            "    TextInput (f) {}" +
+            "  }" +
+            "  TextInput (g) {}" +
+            "  Selection {" +
+            "    TextInput (h) {}" +
+            "    TextInput (i) {}" +
+            "    TextInput (j) {}" +
+            "    TextInput (k) {}" +
+            "  }" +
+            "  Sequence {" +
+            "    TextInput (l) {}" +
+            "    Sequence {" +
+            "      TextInput (m) {}" +
+            "      TextInput (n) {}" +
+            "      TextInput (o) {}" +
+            "      TextInput (p) {}" +
+            "    }" +
+            "    Iteration {" +
+            "      TextInput (q) {}" +
+            "    }" +
+            "    TextInput (r) {}" +
+            "    Selection {" +
+            "      TextInput (s) {}" +
+            "      TextInput (t) {}" +
+            "      TextInput (u) {}" +
+            "      TextInput (v) {}" +
+            "    }" +
+            "  }" +
+            "  Selection {" +
+            "    TextInput (w) {}" +
+            "    Sequence {" +
+            "      TextInput (x) {}" +
+            "      TextInput (y) {}" +
+            "      TextInput (z) {}" +
+            "      TextInput (aa) {}" +
+            "    }" +
+            "    Iteration {" +
+            "      TextInput (ab) {}" +
+            "    }" +
+            "    TextInput (ac) {}" +
+            "    Selection {" +
+            "      TextInput (ad) {}" +
+            "      TextInput (ae) {}" +
+            "      TextInput (af) {}" +
+            "      TextInput (ag) {}" +
+            "    }" +
+            "  }" +
+            "  TextInput (ah) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testCombinationsOfTextInputInteractionsAndOtherInteractions() {
+        UsabilityEvaluationManager manager = new UsabilityEvaluationManager();
+
+        // ===== check =====
+        String spec =
+            "Sequence {" +
+            "  Interaction {}" +
+            "  TextInput (a) {}" +
+            "  TextInput (b) {}" +
+            "  Interaction {}" +
+            "  TextInput (c) {}" +
+            "}";
+
+        UsabilityDefect[] expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(LOW, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  Interaction {}" +
+            "  TextInput (a) {}" +
+            "  Interaction {}" +
+            "  Interaction {}" +
+            "  TextInput (c) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(INFO, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  Interaction {}" +
+            "  TextInput (a) {}" +
+            "  Interaction {}" +
+            "  Interaction {}" +
+            "  Interaction {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[0];
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Selection {" +
+            "  Interaction {}" +
+            "  TextInput (a) {}" +
+            "  TextInput (b) {}" +
+            "  Interaction {}" +
+            "  TextInput (c) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(LOW, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a) {}" +
+            "  Sequence {" +
+            "    Interaction {}" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    Interaction {}" +
+            "    TextInput (d) {}" +
+            "  }" +
+            "  Iteration {" +
+            "    TextInput (e) {}" +
+            "  }" +
+            "  Interaction {}" +
+            "  Selection {" +
+            "    Interaction {}" +
+            "    TextInput (f) {}" +
+            "    TextInput (g) {}" +
+            "    Interaction {}" +
+            "    TextInput (h) {}" +
+            "    TextInput (i) {}" +
+            "  }" +
+            "  Sequence {" +
+            "    TextInput (j) {}" +
+            "    Sequence {" +
+            "      TextInput (k) {}" +
+            "      Interaction {}" +
+            "      TextInput (l) {}" +
+            "      TextInput (m) {}" +
+            "      Interaction {}" +
+            "      TextInput (n) {}" +
+            "      TextInput (o) {}" +
+            "    }" +
+            "    Iteration {" +
+            "      Interaction {}" +
+            "    }" +
+            "    Interaction {}" +
+            "    Selection {" +
+            "      TextInput (p) {}" +
+            "      TextInput (q) {}" +
+            "      TextInput (r) {}" +
+            "      Interaction {}" +
+            "      TextInput (s) {}" +
+            "      TextInput (t) {}" +
+            "      Interaction {}" +
+            "      TextInput (u) {}" +
+            "      TextInput (v) {}" +
+            "    }" +
+            "  }" +
+            "  Selection {" +
+            "    Interaction {}" +
+            "    Sequence {" +
+            "      TextInput (w) {}" +
+            "      Interaction {}" +
+            "      TextInput (x) {}" +
+            "      TextInput (y) {}" +
+            "      Interaction {}" +
+            "    }" +
+            "    Iteration {" +
+            "      TextInput (z) {}" +
+            "    }" +
+            "    TextInput (aa) {}" +
+            "    Selection {" +
+            "      TextInput (ab) {}" +
+            "      Interaction {}" +
+            "      TextInput (ac) {}" +
+            "      TextInput (ad) {}" +
+            "      Interaction {}" +
+            "      TextInput (ae) {}" +
+            "    }" +
+            "  }" +
+            "  Interaction {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(LOW, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a) {}" +
+            "  Sequence {" +
+            "    Interaction {}" +
+            "    TextInput (b) {}" +
+            "    Interaction {}" +
+            "    Interaction {}" +
+            "    TextInput (c) {}" +
+            "  }" +
+            "  Iteration {" +
+            "    TextInput (d) {}" +
+            "  }" +
+            "  Interaction {}" +
+            "  Selection {" +
+            "    Interaction {}" +
+            "    TextInput (e) {}" +
+            "    Interaction {}" +
+            "    Interaction {}" +
+            "    TextInput (g) {}" +
+            "    Interaction {}" +
+            "  }" +
+            "  Sequence {" +
+            "    TextInput (i) {}" +
+            "    Sequence {" +
+            "      TextInput (j) {}" +
+            "      Interaction {}" +
+            "      TextInput (k) {}" +
+            "      Interaction {}" +
+            "      Interaction {}" +
+            "      TextInput (m) {}" +
+            "      Interaction {}" +
+            "    }" +
+            "    Iteration {" +
+            "      Interaction {}" +
+            "    }" +
+            "    Interaction {}" +
+            "    Selection {" +
+            "      TextInput (o) {}" +
+            "      Interaction {}" +
+            "      Interaction {}" +
+            "      Interaction {}" +
+            "      Interaction {}" +
+            "      TextInput (s) {}" +
+            "      Interaction {}" +
+            "      TextInput (t) {}" +
+            "      TextInput (u) {}" +
+            "    }" +
+            "  }" +
+            "  Selection {" +
+            "    Interaction {}" +
+            "    Sequence {" +
+            "      TextInput (v) {}" +
+            "      Interaction {}" +
+            "      Interaction {}" +
+            "      TextInput (x) {}" +
+            "      Interaction {}" +
+            "    }" +
+            "    Iteration {" +
+            "      TextInput (y) {}" +
+            "    }" +
+            "    TextInput (z) {}" +
+            "    Selection {" +
+            "      TextInput (aa) {}" +
+            "      Interaction {}" +
+            "      TextInput (ab) {}" +
+            "      Interaction {}" +
+            "      Interaction {}" +
+            "      TextInput (ad) {}" +
+            "    }" +
+            "  }" +
+            "  Interaction {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(INFO, TEXT_FIELD_INPUT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testTextEntryRepetitions() {
+        UsabilityEvaluationManager manager = new UsabilityEvaluationManager();
+
+        // ===== check =====
+        String spec =
+            "Sequence {" +
+            "  TextInput (a b c) {}" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    TextInput (a) {}" +
+            "  }" +
+            "  Iteration {" +
+            "    TextInput (a) {}" +
+            "  }" +
+            "  TextInput (a) {}" +
+            "  Selection {" +
+            "    TextInput (b c) {}" +
+            "    TextInput (a) {}" +
+            "    TextInput (a c) {}" +
+            "    TextInput (b a) {}" +
+            "  }" +
+            "  Sequence {" +
+            "    TextInput (b c) {}" +
+            "    Sequence {" +
+            "      TextInput (d a c) {}" +
+            "      TextInput (b b b a) {}" +
+            "      TextInput (a a c c) {}" +
+            "      TextInput (b b a) {}" +
+            "    }" +
+            "  }" +
+            "  TextInput (d) {}" +
+            "}";
+
+        UsabilityDefect[] expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_REPETITIONS) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a b c d e f g h i j k l m) {}" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    TextInput (d) {}" +
+            "  }" +
+            "  Iteration {" +
+            "    TextInput (e) {}" +
+            "  }" +
+            "  TextInput (f) {}" +
+            "  Selection {" +
+            "    TextInput (g) {}" +
+            "    TextInput (h) {}" +
+            "    TextInput (i) {}" +
+            "    TextInput (j) {}" +
+            "  }" +
+            "  Sequence {" +
+            "    TextInput (k) {}" +
+            "    Sequence {" +
+            "      TextInput (l) {}" +
+            "      TextInput (m) {}" +
+            "      TextInput (n) {}" +
+            "      TextInput (o) {}" +
+            "    }" +
+            "  }" +
+            "  TextInput (p) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_REPETITIONS) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a b c d e f g h i j k l m) {}" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    TextInput (d) {}" +
+            "  }" +
+            "  Iteration {" +
+            "    TextInput (e) {}" +
+            "  }" +
+            "  TextInput (a) {}" +
+            "  Selection {" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    TextInput (d) {}" +
+            "    TextInput (e) {}" +
+            "  }" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    Sequence {" +
+            "      TextInput (b) {}" +
+            "      TextInput (c) {}" +
+            "      TextInput (d) {}" +
+            "      TextInput (e) {}" +
+            "    }" +
+            "  }" +
+            "  TextInput (f) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(MEDIUM, TEXT_FIELD_INPUT_REPETITIONS) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a b c d e f g h i j k l m) {}" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    TextInput (a) {}" +
+            "  }" +
+            "  Iteration {" +
+            "    TextInput (b) {}" +
+            "  }" +
+            "  TextInput (c) {}" +
+            "  Selection {" +
+            "    TextInput (a) {}" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "    TextInput (a) {}" +
+            "  }" +
+            "  Sequence {" +
+            "    TextInput (b) {}" +
+            "    Sequence {" +
+            "      TextInput (c) {}" +
+            "      TextInput (a) {}" +
+            "      TextInput (b) {}" +
+            "      TextInput (c) {}" +
+            "    }" +
+            "  }" +
+            "  TextInput (a) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(MEDIUM, TEXT_FIELD_INPUT_REPETITIONS) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a b c) {}" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    TextInput (b) {}" +
+            "    TextInput (c) {}" +
+            "  }" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(LOW, TEXT_FIELD_INPUT_REPETITIONS) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a b c) {}" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    TextInput (a) {}" +
+            "    TextInput (b) {}" +
+            "  }" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(LOW, TEXT_FIELD_INPUT_REPETITIONS) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (a b c) {}" +
+            "  Sequence {" +
+            "    TextInput (a) {}" +
+            "    TextInput (d) {}" +
+            "    TextInput (e) {}" +
+            "  }" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(INFO, TEXT_FIELD_INPUT_REPETITIONS) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testNoLetterOrDigitInput() {
+        UsabilityEvaluationManager manager = new UsabilityEvaluationManager();
+
+        // ===== check =====
+        String spec =
+            "Sequence {" +
+            "  TextInput (_a_b_c_) {}" +
+            "}";
+
+        UsabilityDefect[] expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(HIGH, TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (12345_6789012345) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(MEDIUM, TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (123456789012345678901234567890_123456789012345) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(LOW, TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+        // ===== check =====
+        spec =
+            "Sequence {" +
+            "  TextInput (1234567890123456789012345678901234567890123456789_01234567890" +
+            "12345678901234567890123456789012345) {}" +
+            "}";
+
+        expectedDefects = new UsabilityDefect[]
+            { new UsabilityDefect(HIGH, TEXT_FIELD_INPUT_RATIO),
+              new UsabilityDefect(INFO, TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO) };
+
+        assertUsabilityEvaluationResult
+            (expectedDefects, manager.evaluateUsability(createTaskTree(spec)));
+
+    }
+}
Index: trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescriptionTest.java
===================================================================
--- trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescriptionTest.java	(revision 922)
+++ trunk/autoquest-core-usability-test/src/test/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescriptionTest.java	(revision 922)
@@ -0,0 +1,53 @@
+package de.ugoe.cs.autoquest.usability;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.usability.UsabilityDefectDescription;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 20.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class UsabilityDefectDescriptionTest {
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testInitialization() {
+        for (UsabilityDefectDescription description : UsabilityDefectDescription.values()) {
+            assertNotNull(description.toString());
+            assertNotSame("", description.toString());
+            System.err.println(description);
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testParameterization() {
+        for (UsabilityDefectDescription description : UsabilityDefectDescription.values()) {
+            Map<String, String> parameters = new HashMap<String, String>();
+
+            for (String parameter : description.getDescriptionParameters()) {
+                parameters.put(parameter, "<parameter " + parameter + ">");
+            }
+
+            assertNotNull(description.toString(parameters));
+            assertNotSame("", description.toString(parameters));
+            System.err.println(description.toString(parameters));
+        }
+    }
+
+}
Index: trunk/autoquest-core-usability/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-core-usability/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-core-usability/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java	(revision 922)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java	(revision 922)
@@ -0,0 +1,400 @@
+package de.ugoe.cs.autoquest.usability;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class TextInputStatisticsRule implements de.ugoe.cs.autoquest.usability.UsabilityEvaluationRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
+     */
+    @Override
+    public UsabilityEvaluationResult evaluate(ITaskTree taskTree) {
+        TextInputStatistics statistics = new TextInputStatistics();
+        calculateStatistics(taskTree.getRoot(), statistics);
+
+        UsabilityEvaluationResult results = new UsabilityEvaluationResult();
+        analyzeStatistics(statistics, results);
+
+        return results;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param statistics
+     * @param results
+     */
+    private void analyzeStatistics(TextInputStatistics       statistics,
+                                   UsabilityEvaluationResult results)
+    {
+        checkTextInputRatio(statistics, results);
+        checkTextFieldEntryRepetitions(statistics, results);
+        checkTextFieldNoLetterOrDigitInputs(statistics, results);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param statistics
+     * @param results
+     */
+    private void checkTextInputRatio(TextInputStatistics       statistics,
+                                     UsabilityEvaluationResult results)
+    {
+        float allTextFieldInputs =
+            statistics.getNoOfTextFieldInputs() + statistics.getNoOfTextAreaInputs();
+
+        float ratio = allTextFieldInputs / (float) statistics.getNoOfAllEvents();
+
+        UsabilityDefectSeverity severity = null;
+        if (ratio > 0.9) {
+            severity = UsabilityDefectSeverity.HIGH;
+        }
+        else if (ratio > 0.7) {
+            severity = UsabilityDefectSeverity.MEDIUM;
+        }
+        else if (ratio > 0.5) {
+            severity = UsabilityDefectSeverity.LOW;
+        }
+        else if (ratio > 0.3) {
+            severity = UsabilityDefectSeverity.INFO;
+        }
+
+        if (severity != null) {
+            Map<String, String> parameters = new HashMap<String, String>();
+            parameters.put("textInputRatio", DecimalFormat.getInstance().format(ratio * 100) + "%");
+
+            results.addDefect
+                (new UsabilityDefect(severity, UsabilityDefectDescription.TEXT_FIELD_INPUT_RATIO,
+                                     parameters));
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param statistics
+     * @param results
+     */
+    private void checkTextFieldEntryRepetitions(TextInputStatistics       statistics,
+                                                UsabilityEvaluationResult results)
+    {
+        Map<String, Integer> words = new HashMap<String, Integer>();
+        int numberOfRepeatedWords = 0;
+        int maxRepetitions = 0;
+
+        for (int i = 0; i < statistics.getNoOfTextFieldInputs(); i++) {
+            String[] fragments = statistics.getTextFieldInputFragments(i);
+            for (String fragment : fragments) {
+                if (!"".equals(fragment.trim())) {
+                    Integer count = words.get(fragment);
+                    if (count == null) {
+                        words.put(fragment, 1);
+                    }
+                    else {
+                        count++;
+                        words.put(fragment, count);
+                        maxRepetitions = Math.max(count, maxRepetitions);
+
+                        if (count == 2) {
+                            // do not calculate repeated words several times
+                            numberOfRepeatedWords++;
+                        }
+                    }
+                }
+            }
+        }
+
+        UsabilityDefectSeverity severity = null;
+        if ((numberOfRepeatedWords > 10) || (maxRepetitions > 10)) {
+            severity = UsabilityDefectSeverity.HIGH;
+        }
+        else if ((numberOfRepeatedWords > 4) || (maxRepetitions > 4)) {
+            severity = UsabilityDefectSeverity.MEDIUM;
+        }
+        else if ((numberOfRepeatedWords > 2) || (maxRepetitions > 2)) {
+            severity = UsabilityDefectSeverity.LOW;
+        }
+        else if ((numberOfRepeatedWords > 1) || (maxRepetitions > 1)) {
+            severity = UsabilityDefectSeverity.INFO;
+        }
+
+        if (severity != null) {
+            Map<String, String> parameters = new HashMap<String, String>();
+            parameters.put("textRepetitionRatio", numberOfRepeatedWords +
+                           " repeated tokens, up to " + maxRepetitions + " repetitions per token");
+
+            results.addDefect
+                (new UsabilityDefect(severity,
+                                     UsabilityDefectDescription.TEXT_FIELD_INPUT_REPETITIONS,
+                                     parameters));
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param statistics
+     * @param results
+     */
+    private void checkTextFieldNoLetterOrDigitInputs(TextInputStatistics       statistics,
+                                                     UsabilityEvaluationResult results)
+    {
+        int allCharactersCount = 0;
+        int noLetterOrDigitCount = 0;
+
+        for (int i = 0; i < statistics.getNoOfTextFieldInputs(); i++) {
+            String[] fragments = statistics.getTextFieldInputFragments(i);
+            for (String fragment : fragments) {
+                String effectiveFragment = fragment.trim();
+                for (int j = 0; j < effectiveFragment.length(); j++) {
+                    if (!Character.isWhitespace(effectiveFragment.charAt(j))) {
+                        if (!Character.isLetterOrDigit(effectiveFragment.charAt(j))) {
+                            noLetterOrDigitCount++;
+                        }
+                        allCharactersCount++;
+                    }
+                }
+            }
+        }
+
+        float ratio = (float) noLetterOrDigitCount / (float) allCharactersCount;
+
+        UsabilityDefectSeverity severity = null;
+        if (ratio > 0.1) // every 10th sign
+        {
+            severity = UsabilityDefectSeverity.HIGH;
+        }
+        else if (ratio > 0.05) // every 20th sign
+        {
+            severity = UsabilityDefectSeverity.MEDIUM;
+        }
+        else if (ratio > 0.02) // every 50th sign
+        {
+            severity = UsabilityDefectSeverity.LOW;
+        }
+        else if (ratio > 0.01) // every 100th sign
+        {
+            severity = UsabilityDefectSeverity.INFO;
+        }
+
+        if (severity != null) {
+            Map<String, String> parameters = new HashMap<String, String>();
+            parameters.put("noLetterOrDigitRatio", allCharactersCount + " entered characters of " +
+                           "which " + noLetterOrDigitCount + " were no letter or digit");
+
+            results.addDefect
+                (new UsabilityDefect(severity,
+                                     UsabilityDefectDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
+                                     parameters));
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param taskTree
+     * @param statistics
+     */
+    private void calculateStatistics(ITaskTreeNode node, TextInputStatistics statistics) {
+        if ((node instanceof IEventTask) &&
+            (((IEventTask) node).getEventType() instanceof TextInput))
+        {
+            calculateStatistics((IEventTask) node, statistics);
+        }
+        else {
+            if ((node.getChildren() == null) || (node.getChildren().size() == 0)) {
+                statistics.incrementNoOfOtherEventTasks();
+            }
+            else {
+                for (ITaskTreeNode child : node.getChildren()) {
+                    calculateStatistics(child, statistics);
+                }
+            }
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param taskTree
+     * @param statistics
+     */
+    private void calculateStatistics(IEventTask node, TextInputStatistics statistics) {
+        String[] fragments =
+            determineTextFragments(((TextInput) node.getEventType()).getEnteredText());
+
+        if (node.getEventTarget() instanceof ITextField) {
+            statistics.addTextFieldInput(node, fragments);
+        }
+        else if (node.getEventTarget() instanceof ITextArea) {
+            statistics.addTextAreaInput(node, fragments);
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param enteredText
+     * @return
+     */
+    private String[] determineTextFragments(String enteredText) {
+        List<String> fragments = new ArrayList<String>();
+
+        StringBuffer fragment = new StringBuffer();
+        char lastChar = 0;
+
+        for (int i = 0; i < enteredText.length(); i++) {
+            char currentChar = enteredText.charAt(i);
+
+            if (!isEqualCharacterType(lastChar, currentChar)) {
+                // the previous fragment ended. so finalize it and start a new one
+                if ((fragment != null) && (fragment.length() > 0)) {
+                    fragments.add(fragment.toString());
+                    fragment = new StringBuffer();
+                }
+            }
+
+            fragment.append(currentChar);
+            lastChar = currentChar;
+        }
+
+        if ((fragment != null) && (fragment.length() > 0)) {
+            fragments.add(fragment.toString());
+        }
+
+        return fragments.toArray(new String[fragments.size()]);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param lastChar
+     * @param currentChar
+     * @return
+     */
+    private boolean isEqualCharacterType(char char1, char char2) {
+        return
+            ((char1 == char2) ||
+            (Character.isWhitespace(char1) && Character.isWhitespace(char2)) ||
+            (Character.isDigit(char1) && Character.isDigit(char2)) ||
+            (Character.isLetter(char1) && Character.isLetter(char2)) ||
+            (Character.isJavaIdentifierPart(char1) && Character.isJavaIdentifierPart(char2)));
+    }
+
+    /**
+     * TODO comment
+     * 
+     * @version $Revision: $ $Date: 16.07.2012$
+     * @author 2012, last modified by $Author: pharms$
+     */
+    public static class TextInputStatistics {
+        
+        /** */
+        private List<Object[]> textFieldInputs = new ArrayList<Object[]>();
+
+        /** */
+        private List<Object[]> textAreaInputs = new ArrayList<Object[]>();
+
+        /** */
+        private int otherEventsCount;
+
+        /**
+         * TODO: comment
+         * 
+         * @param node
+         * @param fragments
+         * 
+         */
+        public void addTextFieldInput(IEventTask node, String[] fragments) {
+            textFieldInputs.add(new Object[] { node, fragments });
+        }
+
+        /**
+         * TODO: comment
+         * 
+         * @param node
+         * @param fragments
+         * 
+         */
+        public void addTextAreaInput(IEventTask node, String[] fragments) {
+            textAreaInputs.add(new Object[] { node, fragments });
+        }
+
+        /**
+         * TODO: comment
+         * 
+         * @return
+         */
+        public int getNoOfAllEvents() {
+            return textFieldInputs.size() + textAreaInputs.size() + otherEventsCount;
+        }
+
+        /**
+         * TODO: comment
+         * 
+         * @return
+         */
+        public int getNoOfTextFieldInputs() {
+            return textFieldInputs.size();
+        }
+
+        /**
+         * TODO: comment
+         * 
+         * @param i
+         * @return
+         */
+        public String[] getTextFieldInputFragments(int index) {
+            return (String[]) textFieldInputs.get(index)[1];
+        }
+
+        /**
+         * TODO: comment
+         * 
+         * @return
+         */
+        public int getNoOfTextAreaInputs() {
+            return textAreaInputs.size();
+        }
+
+        /**
+         * TODO: comment
+         * 
+         * @param i
+         * @return
+         */
+        public String[] getTextAreaInputFragments(int index) {
+            return (String[]) textAreaInputs.get(index)[1];
+        }
+
+        /**
+         * TODO: comment
+         * 
+         */
+        public void incrementNoOfOtherEventTasks() {
+            otherEventsCount++;
+        }
+
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefect.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefect.java	(revision 922)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefect.java	(revision 922)
@@ -0,0 +1,117 @@
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.Map;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class UsabilityDefect {
+
+    /** */
+    private UsabilityDefectSeverity severity;
+
+    /** */
+    private UsabilityDefectDescription description;
+
+    /** */
+    private Map<String, String> descriptionParameters;
+
+    /**
+     * TODO: comment
+     * 
+     * @param medium
+     * @param highTextInputRatio
+     */
+    public UsabilityDefect(UsabilityDefectSeverity severity, UsabilityDefectDescription description)
+    {
+        this(severity, description, null);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param medium
+     * @param highTextInputRatio
+     */
+    public UsabilityDefect(UsabilityDefectSeverity    severity,
+                           UsabilityDefectDescription description,
+                           Map<String, String>        parameters)
+    {
+        this.severity = severity;
+        this.description = description;
+        this.descriptionParameters = parameters;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public UsabilityDefectSeverity getSeverity() {
+        return severity;
+    }
+
+    /**
+     * @param severity
+     *            the severity to set
+     */
+    public void setSeverity(UsabilityDefectSeverity severity) {
+        this.severity = severity;
+    }
+
+    /**
+     * @param description
+     *            the description to set
+     */
+    public void setDescription(UsabilityDefectDescription description) {
+        this.description = description;
+    }
+
+    /**
+   * 
+   */
+    public String getParameterizedDescription() {
+        return description.toString(descriptionParameters);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof UsabilityDefect) {
+            return
+                (severity == ((UsabilityDefect) obj).severity) &&
+                (description == ((UsabilityDefect) obj).description);
+        }
+        else {
+            return false;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return severity.hashCode() + description.hashCode();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "UsabilityDefect(" + severity.name() + ", " + description.name() + ")";
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescription.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescription.java	(revision 922)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescription.java	(revision 922)
@@ -0,0 +1,191 @@
+package de.ugoe.cs.autoquest.usability;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Unmarshaller;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public enum UsabilityDefectDescription {
+    
+    TEXT_FIELD_INPUT_RATIO,
+    TEXT_FIELD_INPUT_REPETITIONS,
+    TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO;
+
+    /** */
+    private static final String DEFAULT_MESSAGES_FILE = "defectDescriptions_en.xml";
+
+    /** */
+    private static DefectDescriptions sDefectDescriptions;
+
+    /** */
+    private DefectDescription defectDescription;
+
+    /**
+     * TODO: comment
+     * 
+     * @param name
+     * @param ordinal
+     */
+    private UsabilityDefectDescription() {
+        init();
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @SuppressWarnings("unchecked")
+    private void init() {
+        synchronized (this.getClass()) {
+            if (sDefectDescriptions == null) {
+                InputStream inputStream =
+                    ClassLoader.getSystemResourceAsStream(DEFAULT_MESSAGES_FILE);
+
+                try {
+                    String packageName = DefectDescriptions.class.getPackage().getName();
+                    JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
+                    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+
+                    sDefectDescriptions =
+                        ((JAXBElement<DefectDescriptions>) unmarshaller.unmarshal(inputStream))
+                            .getValue();
+                }
+                catch (Exception e) {
+                    throw new RuntimeException
+                        ("error while initializing usability defect descriptions", e);
+                }
+                finally {
+                    if (inputStream != null) {
+                        try {
+                            inputStream.close();
+                        }
+                        catch (IOException e) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+        }
+
+        for (DefectDescription description : sDefectDescriptions.getDefectDescription()) {
+            if (this.name().equals(description.getDefectId())) {
+                defectDescription = description;
+                break;
+            }
+        }
+
+        if (defectDescription == null) {
+            throw new RuntimeException
+                ("error while initializing usability defect descriptions. No " +
+                 "description text available for description " + this.name());
+        }
+    }
+
+    /**
+   * 
+   */
+    public String[] getDescriptionParameters() {
+        List<String> parameters = new ArrayList<String>();
+
+        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
+            if (fragment instanceof ParameterFragment) {
+                parameters.add(((ParameterFragment) fragment).getParameterName());
+            }
+        }
+
+        return parameters.toArray(new String[parameters.size()]);
+    }
+
+    /**
+   * 
+   */
+    public String toString(Map<String, String> parameters) throws IllegalArgumentException {
+        StringBuffer result = new StringBuffer();
+
+        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
+            if (result.length() > 0) {
+                result.append(" ");
+            }
+
+            if (fragment instanceof ParameterFragment) {
+                String value = null;
+                if (parameters != null) {
+                    value = parameters.get(((ParameterFragment) fragment).getParameterName());
+                }
+
+                if (value != null) {
+                    result.append(value);
+                }
+                else {
+                    throw new IllegalArgumentException
+                        ("required parameter \"" +
+                         ((ParameterFragment) fragment).getParameterName() +
+                         "\" for usability defect description " + this.name() + " not provided");
+                }
+            }
+            else {
+                result.append(getFragmentString(fragment));
+            }
+        }
+
+        return result.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Enum#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuffer result = new StringBuffer();
+
+        int paramCount = 1;
+        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
+            if (result.length() > 0) {
+                result.append(" ");
+            }
+
+            if (fragment instanceof ParameterFragment) {
+                result.append("<parameter");
+                result.append(paramCount++);
+                result.append(">");
+            }
+            else {
+                result.append(getFragmentString(fragment));
+            }
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param fragment
+     * @return
+     */
+    private String getFragmentString(Object fragment) {
+        String fragmentStr = fragment.toString().trim();
+
+        fragmentStr = fragmentStr.replaceAll("\n", " ");
+
+        while (fragmentStr.indexOf("  ") > -1) {
+            fragmentStr = fragmentStr.replaceAll("  ", " ");
+        }
+
+        return fragmentStr;
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectSeverity.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectSeverity.java	(revision 922)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectSeverity.java	(revision 922)
@@ -0,0 +1,13 @@
+package de.ugoe.cs.autoquest.usability;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public enum UsabilityDefectSeverity {
+    
+    INFO, LOW, MEDIUM, HIGH;
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java	(revision 922)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java	(revision 922)
@@ -0,0 +1,81 @@
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class UsabilityEvaluationManager {
+    
+    /** */
+    private List<UsabilityEvaluationRule> rules = new ArrayList<UsabilityEvaluationRule>();
+
+    /**
+     * TODO: comment
+     * 
+     */
+    public UsabilityEvaluationManager() {
+        super();
+        init();
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    private void init() {
+        rules.add(new TextInputStatisticsRule());
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param taskTree
+     */
+    public UsabilityEvaluationResult evaluateUsability(ITaskTree taskTree) {
+        Console.traceln(Level.INFO, "evaluating usability of task tree " + taskTree);
+
+        List<UsabilityEvaluationResult> results = new ArrayList<UsabilityEvaluationResult>();
+
+        for (UsabilityEvaluationRule rule : rules) {
+            Console.traceln(Level.INFO, "applying rule " + rule.getClass().getSimpleName());
+            UsabilityEvaluationResult result = rule.evaluate(taskTree);
+            results.add(result);
+            Console.traceln(Level.INFO, "the rule found " + result.getAllDefects().size() +
+                            " usability defects, of which " + result.getSevereDefects().size() +
+                            " are severe.");
+        }
+
+        UsabilityEvaluationResult result = mergeResults(results);
+        Console.println("the evaluation result contains " + result.getAllDefects().size() +
+                        " defects, of which " + result.getSevereDefects().size() + " are severe.");
+        return result;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param results
+     * @return
+     */
+    private UsabilityEvaluationResult mergeResults(List<UsabilityEvaluationResult> results) {
+        UsabilityEvaluationResult result = new UsabilityEvaluationResult();
+
+        for (UsabilityEvaluationResult ruleResult : results) {
+            for (UsabilityDefect defect : ruleResult.getAllDefects()) {
+                result.addDefect(defect);
+            }
+        }
+
+        return result;
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java	(revision 922)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java	(revision 922)
@@ -0,0 +1,52 @@
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class UsabilityEvaluationResult {
+    
+    /** */
+    private List<UsabilityDefect> defects = new ArrayList<UsabilityDefect>();
+
+    /**
+     * TODO: comment
+     * 
+     * @param defect
+     */
+    public void addDefect(UsabilityDefect defect) {
+        defects.add(defect);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public List<UsabilityDefect> getAllDefects() {
+        return defects;
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @return
+     */
+    public List<UsabilityDefect> getSevereDefects() {
+        List<UsabilityDefect> severeDefects = new ArrayList<UsabilityDefect>();
+
+        for (UsabilityDefect defect : defects) {
+            if (defect.getSeverity() == UsabilityDefectSeverity.HIGH) {
+                severeDefects.add(defect);
+            }
+        }
+
+        return severeDefects;
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationRule.java	(revision 922)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationRule.java	(revision 922)
@@ -0,0 +1,21 @@
+package de.ugoe.cs.autoquest.usability;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public interface UsabilityEvaluationRule {
+
+    /**
+     * TODO: comment
+     * 
+     * @param taskTree
+     * @return
+     */
+    UsabilityEvaluationResult evaluate(ITaskTree taskTree);
+
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomatonTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomatonTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomatonTest.java	(revision 922)
@@ -0,0 +1,158 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.DeterministicFiniteAutomaton;
+
+import java.util.Random;
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>DeterministicFiniteAutomatonTest</code> contains tests for
+ * the class <code>{@link DeterministicFiniteAutomaton}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class DeterministicFiniteAutomatonTest {
+
+	Collection<List<Event>> sequences;
+
+	@Test
+	public void testDeterministicFiniteAutomaton_1() throws Exception {
+		Random r = new Random();
+
+		DeterministicFiniteAutomaton result = new DeterministicFiniteAutomaton(
+				r);
+
+		assertNotNull(result);
+		assertEquals(2, result.trieOrder);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testDeterministicFiniteAutomaton_2() throws Exception {
+		new DeterministicFiniteAutomaton(null);
+	}
+
+	@Test
+	public void testGetProbability_1() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+		context.add(new Event(new StringEventType("b")));
+
+		Event symbol = new Event(new StringEventType("r"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(1.0d, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_2() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("b"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(1.0d / 4.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_3() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("c"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(1.0d / 4.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_4() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("e"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(0.0d, result, 0.0001);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testGetProbability_5() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = null;
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testGetProbability_6() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = null;
+
+		Event symbol = new Event(new StringEventType("a"));
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		List<Event> sequence = new ArrayList<Event>();
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("c")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("d")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+
+		sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore()
+				.run(DeterministicFiniteAutomatonTest.class);
+	}
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModelTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModelTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModelTest.java	(revision 922)
@@ -0,0 +1,95 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel.MarkovEdge;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>FirstOrderMarkovModelTest</code> contains tests for the class
+ * <code>{@link FirstOrderMarkovModel}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class FirstOrderMarkovModelTest {
+
+	Collection<List<Event>> sequences;
+	
+	@Test
+	public void testFirstOrderMarkovModel_1() throws Exception {
+		Random r = new Random();
+
+		FirstOrderMarkovModel result = new FirstOrderMarkovModel(r);
+
+		assertNotNull(result);
+		assertEquals(r, result.r);
+		assertEquals(2, result.trieOrder);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testFirstOrderMarkovModel_2() throws Exception {
+		new FirstOrderMarkovModel(null);
+	}
+	
+	@Test
+	public void testCalcEntropy() throws Exception {
+		Random r = new Random();
+		FirstOrderMarkovModel fixture = new FirstOrderMarkovModel(r);
+		fixture.train(sequences);
+		
+		double result = fixture.calcEntropy();
+		
+		assertEquals(0.7392d, result, 0.0001);
+	}
+	
+	@Test
+	public void testMarkovEdgeMarkovEdge_1() throws Exception {
+		double weight = 0.2d;
+		
+		MarkovEdge result = new MarkovEdge(weight);
+		
+		assertNotNull(result);
+		assertEquals(weight, result.weight, 0.0001);
+	}
+	
+	@Test
+	public void testMarkovEdgeToString_1() throws Exception {
+		double weight = 0.2d;
+		MarkovEdge fixture = new MarkovEdge(weight);
+		
+		String result = fixture.toString();
+		
+		assertEquals(Double.toString(0.2d), result);
+	}
+	
+	@Before
+	public void setUp() throws Exception {
+		List<Event> sequence = new ArrayList<Event>();
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("c")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("d")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+
+		sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(FirstOrderMarkovModelTest.class);
+	}
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModelTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModelTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModelTest.java	(revision 922)
@@ -0,0 +1,242 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.HighOrderMarkovModel;
+
+import java.util.Random;
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>HighOrderMarkovModelTest</code> contains tests for the class
+ * <code>{@link HighOrderMarkovModel}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class HighOrderMarkovModelTest {
+
+	Collection<List<Event>> sequences;
+
+	@Test
+	public void testHighOrderMarkovModel_1() throws Exception {
+		int maxOrder = 1;
+		Random r = new Random();
+
+		HighOrderMarkovModel result = new HighOrderMarkovModel(maxOrder, r);
+
+		assertNotNull(result);
+		assertEquals(r, result.r);
+		assertEquals(maxOrder + 1, result.trieOrder);
+	}
+
+	@Test
+	public void testHighOrderMarkovModel_2() throws Exception {
+		int maxOrder = 0;
+		Random r = new Random();
+
+		HighOrderMarkovModel result = new HighOrderMarkovModel(maxOrder, r);
+
+		assertNotNull(result);
+		assertEquals(r, result.r);
+		assertEquals(maxOrder + 1, result.trieOrder);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testHighOrderMarkovModel_3() throws Exception {
+		int maxOrder = 1;
+		Random r = null;
+
+		new HighOrderMarkovModel(maxOrder, r);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testHighOrderMarkovModel_4() throws Exception {
+		int maxOrder = -1;
+		Random r = new Random();
+
+		new HighOrderMarkovModel(maxOrder, r);
+	}
+
+	@Test
+	public void testGetProbability_1() throws Exception {
+		int markovOrder = 1;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("b"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(2.0d / 5.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_2() throws Exception {
+		int markovOrder = 1;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("r"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(0.0d / 5.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_3() throws Exception {
+		int markovOrder = 1;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("c"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(1.0d / 5.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_4() throws Exception {
+		int markovOrder = 1;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("b"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(2.0d / 5.0, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_5() throws Exception {
+		int markovOrder = 2;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("b"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(1.0d, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_6() throws Exception {
+		int markovOrder = 2;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("b")));
+
+		Event symbol = new Event(new StringEventType("b"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(0.0d, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_7() throws Exception {
+		int markovOrder = 0;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("b")));
+
+		Event symbol = new Event(new StringEventType("a"));
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(5.0d / 13.0, result, 0.0001);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testGetProbability_8() throws Exception {
+		int markovOrder = 0;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("b")));
+
+		Event symbol = null;
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testGetProbability_9() throws Exception {
+		int markovOrder = 0;
+		HighOrderMarkovModel fixture = new HighOrderMarkovModel(markovOrder,
+				new Random());
+		fixture.train(sequences);
+
+		List<Event> context = null;
+
+		Event symbol = new Event(new StringEventType("b"));
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		List<Event> sequence = new ArrayList<Event>();
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("c")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("d")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+
+		sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(HighOrderMarkovModelTest.class);
+	}
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemoryTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemoryTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemoryTest.java	(revision 922)
@@ -0,0 +1,152 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.usageprofiles.IncompleteMemory;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>IncompleteMemoryTest</code> contains tests for the class <code>{@link IncompleteMemory}</code>.
+ *
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class IncompleteMemoryTest {
+
+	@Test
+	public void testIncompleteMemory_1()
+		throws Exception {
+		int length = 1;
+
+		IncompleteMemory<String> result = new IncompleteMemory<String>(length);
+
+		assertNotNull(result);
+		assertEquals(0, result.getLast(1).size());
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testIncompleteMemory_2()
+		throws Exception {
+		int length = 0;
+
+		new IncompleteMemory<String>(length);
+	}
+
+	@Test
+	public void testGetLast_1()
+		throws Exception {
+		int length = 2;
+		IncompleteMemory<String> fixture = new IncompleteMemory<String>(length);
+		fixture.add("1");
+		fixture.add("2");
+		fixture.add("3");
+		int num = -1;
+
+		List<String> result = fixture.getLast(num);
+
+		assertNotNull(result);
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetLast_2()
+		throws Exception {
+		int length = 2;
+		IncompleteMemory<String> fixture = new IncompleteMemory<String>(length);
+		fixture.add("1");
+		fixture.add("2");
+		fixture.add("3");
+		int num = 1;
+		
+		List<String> expected = new ArrayList<String>();
+		expected.add("3");
+
+		List<String> result = fixture.getLast(num);
+
+		assertNotNull(result);
+		assertEquals(expected, result);
+	}
+
+	@Test
+	public void testGetLast_3()
+		throws Exception {
+		int length = 2;
+		IncompleteMemory<String> fixture = new IncompleteMemory<String>(length);
+		fixture.add("1");
+		fixture.add("2");
+		fixture.add("3");
+		int num = 2;
+		
+		List<String> expected = new ArrayList<String>();
+		expected.add("2");
+		expected.add("3");
+
+		List<String> result = fixture.getLast(num);
+
+		assertNotNull(result);
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testGetLast_4()
+		throws Exception {
+		int length = 2;
+		IncompleteMemory<String> fixture = new IncompleteMemory<String>(length);
+		fixture.add("1");
+		fixture.add("2");
+		fixture.add("3");
+		int num = 3;
+		
+		List<String> expected = new ArrayList<String>();
+		expected.add("2");
+		expected.add("3");
+
+		List<String> result = fixture.getLast(num);
+
+		assertNotNull(result);
+		assertEquals(expected, result);
+	}
+
+	@Test
+	public void testGetLength_1()
+		throws Exception {
+		int length = 2;
+		IncompleteMemory<String> fixture = new IncompleteMemory<String>(length);
+		
+		int result = fixture.getLength(); 
+
+		assertEquals(0, result);
+	}
+	
+	@Test
+	public void testGetLength_2()
+		throws Exception {
+		int length = 2;
+		IncompleteMemory<String> fixture = new IncompleteMemory<String>(length);
+		fixture.add("1");
+		
+		int result = fixture.getLength(); 
+
+		assertEquals(1, result);
+	}
+	
+	@Test
+	public void testGetLength_3()
+		throws Exception {
+		int length = 2;
+		IncompleteMemory<String> fixture = new IncompleteMemory<String>(length);
+		fixture.add("1");
+		fixture.add("2");
+		fixture.add("3");
+		
+		int result = fixture.getLength(); 
+
+		assertEquals(2, result);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(IncompleteMemoryTest.class);
+	}
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattenerTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattenerTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattenerTest.java	(revision 922)
@@ -0,0 +1,148 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.HighOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.ModelFlattener;
+import de.ugoe.cs.autoquest.usageprofiles.PredictionByPartialMatch;
+import de.ugoe.cs.autoquest.usageprofiles.TrieNode;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>ModelFlattenerTest</code> contains tests for the class
+ * <code>{@link ModelFlattener}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class ModelFlattenerTest {
+
+    List<Event> sequence;
+
+    private static void assertCollectionContent(Collection<?> c1, Collection<?> c2) {
+        assertEquals(c1.size(), c2.size());
+        for (Object obj : c1) {
+            assertTrue(c2.contains(obj));
+        }
+    }
+
+    @Test
+    public void testFlattenHighOrderMarkovModel_1() throws Exception {
+        ModelFlattener fixture = new ModelFlattener();
+        HighOrderMarkovModel model = new HighOrderMarkovModel(2, new Random());
+        Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+        sequences.add(sequence);
+        model.train(sequences);
+
+        Collection<Event> expectedSymbols = new HashSet<Event>();
+        expectedSymbols.add(new Event(new StringEventType("a-=-END")));
+        expectedSymbols.add(new Event(new StringEventType("a-=-b")));
+        expectedSymbols.add(new Event(new StringEventType("a-=-c")));
+        expectedSymbols.add(new Event(new StringEventType("a-=-d")));
+        expectedSymbols.add(new Event(new StringEventType("b-=-r")));
+        expectedSymbols.add(new Event(new StringEventType("c-=-a")));
+        expectedSymbols.add(new Event(new StringEventType("d-=-a")));
+        expectedSymbols.add(new Event(new StringEventType("r-=-a")));
+        expectedSymbols.add(new Event(new StringEventType("START-=-a")));
+
+        FirstOrderMarkovModel result = fixture.flattenHighOrderMarkovModel(model);
+
+        assertCollectionContent(expectedSymbols, result.getEvents());
+
+        TrieNode<Event> root = result.trie.find(null);
+        TrieNode<Event> root_aEnd = root.getChild(new Event(new StringEventType("a-=-END")));
+        TrieNode<Event> root_ab = root.getChild(new Event(new StringEventType("a-=-b")));
+        TrieNode<Event> root_ab_br = root_ab.getChild(new Event(new StringEventType("b-=-r")));
+        TrieNode<Event> root_ac = root.getChild(new Event(new StringEventType("a-=-c")));
+        TrieNode<Event> root_ac_ca = root_ac.getChild(new Event(new StringEventType("c-=-a")));
+        TrieNode<Event> root_ad = root.getChild(new Event(new StringEventType("a-=-d")));
+        TrieNode<Event> root_ad_da = root_ad.getChild(new Event(new StringEventType("d-=-a")));
+        TrieNode<Event> root_br = root.getChild(new Event(new StringEventType("b-=-r")));
+        TrieNode<Event> root_br_ra = root_br.getChild(new Event(new StringEventType("r-=-a")));
+        TrieNode<Event> root_ca = root.getChild(new Event(new StringEventType("c-=-a")));
+        TrieNode<Event> root_ca_ad = root_ca.getChild(new Event(new StringEventType("a-=-d")));
+        TrieNode<Event> root_da = root.getChild(new Event(new StringEventType("d-=-a")));
+        TrieNode<Event> root_da_ab = root_da.getChild(new Event(new StringEventType("a-=-b")));
+        TrieNode<Event> root_ra = root.getChild(new Event(new StringEventType("r-=-a")));
+        TrieNode<Event> root_ra_ac = root_ra.getChild(new Event(new StringEventType("a-=-c")));
+        TrieNode<Event> root_ra_aEnd = root_ra.getChild(new Event(new StringEventType("a-=-END")));
+        TrieNode<Event> root_startA = root.getChild(new Event(new StringEventType("START-=-a")));
+        TrieNode<Event> root_startA_ab =
+            root_startA.getChild(new Event(new StringEventType("a-=-b")));
+
+        assertEquals(1, root_aEnd.getCount());
+        assertTrue(root_aEnd.isLeaf());
+        assertEquals(2, root_ab.getCount());
+        assertEquals(1, root_ab.getChildren().size());
+        assertEquals(2, root_ab_br.getCount());
+        assertTrue(root_ab_br.isLeaf());
+        assertEquals(1, root_ac.getCount());
+        assertEquals(1, root_ac.getChildren().size());
+        assertEquals(1, root_ac_ca.getCount());
+        assertTrue(root_ac_ca.isLeaf());
+        assertEquals(1, root_ad.getCount());
+        assertEquals(1, root_ad.getChildren().size());
+        assertEquals(1, root_ad_da.getCount());
+        assertTrue(root_ad_da.isLeaf());
+        assertEquals(2, root_br.getCount());
+        assertEquals(1, root_br.getChildren().size());
+        assertEquals(2, root_br_ra.getCount());
+        assertTrue(root_br_ra.isLeaf());
+        assertEquals(1, root_ca.getCount());
+        assertEquals(1, root_ca.getChildren().size());
+        assertEquals(1, root_ca_ad.getCount());
+        assertTrue(root_ca_ad.isLeaf());
+        assertEquals(1, root_da.getCount());
+        assertEquals(1, root_da.getChildren().size());
+        assertEquals(1, root_da_ab.getCount());
+        assertTrue(root_da_ab.isLeaf());
+        assertEquals(2, root_ra.getCount());
+        assertEquals(2, root_ra.getChildren().size());
+        assertEquals(1, root_ra_ac.getCount());
+        assertTrue(root_ra_ac.isLeaf());
+        assertEquals(1, root_ra_aEnd.getCount());
+        assertTrue(root_ra_aEnd.isLeaf());
+        assertEquals(1, root_startA.getCount());
+        assertEquals(1, root_startA.getChildren().size());
+        assertEquals(1, root_startA_ab.getCount());
+        assertTrue(root_startA_ab.isLeaf());
+    }
+
+    @Test
+    public void testFlattenPredictionByPartialMatch_1() throws Exception {
+        ModelFlattener fixture = new ModelFlattener();
+        PredictionByPartialMatch model = new PredictionByPartialMatch(1, new Random());
+
+        FirstOrderMarkovModel result = fixture.flattenPredictionByPartialMatch(model);
+
+        assertEquals(null, result);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        sequence = new ArrayList<Event>();
+        sequence.add(new Event(new StringEventType("a")));
+        sequence.add(new Event(new StringEventType("b")));
+        sequence.add(new Event(new StringEventType("r")));
+        sequence.add(new Event(new StringEventType("a")));
+        sequence.add(new Event(new StringEventType("c")));
+        sequence.add(new Event(new StringEventType("a")));
+        sequence.add(new Event(new StringEventType("d")));
+        sequence.add(new Event(new StringEventType("a")));
+        sequence.add(new Event(new StringEventType("b")));
+        sequence.add(new Event(new StringEventType("r")));
+        sequence.add(new Event(new StringEventType("a")));
+    }
+
+    public static void main(String[] args) {
+        new org.junit.runner.JUnitCore().run(ModelFlattenerTest.class);
+    }
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatchTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatchTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatchTest.java	(revision 922)
@@ -0,0 +1,366 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.PredictionByPartialMatch;
+
+import java.util.Random;
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>PredictionByPartialMatchTest</code> contains tests for the
+ * class <code>{@link PredictionByPartialMatch}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class PredictionByPartialMatchTest {
+
+	Collection<List<Event>> sequences;
+
+	@Test
+	public void testPredictionByPartialMatch_1() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+
+		PredictionByPartialMatch result = new PredictionByPartialMatch(
+				markovOrder, r);
+
+		assertNotNull(result);
+		assertEquals(markovOrder+1, result.trieOrder);
+		assertEquals(0, result.minOrder);
+		assertEquals(r, result.r);
+		assertEquals(0.1, result.probEscape, 0.0001);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_2() throws Exception {
+		int markovOrder = -1;
+		Random r = new Random();
+
+		new PredictionByPartialMatch(markovOrder, r);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_3() throws Exception {
+		int markovOrder = 2;
+		Random r = null;
+
+		new PredictionByPartialMatch(markovOrder, r);
+	}
+	
+	@Test
+	public void testPredictionByPartialMatch_4() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+
+		PredictionByPartialMatch result = new PredictionByPartialMatch(
+				markovOrder, r, probEscape);
+
+		assertNotNull(result);
+		assertEquals(markovOrder+1, result.trieOrder);
+		assertEquals(0, result.minOrder);
+		assertEquals(r, result.r);
+		assertEquals(probEscape, result.probEscape, 0.0001);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_5() throws Exception {
+		int markovOrder = -1;
+		Random r = new Random();
+		double probEscape = 0.2;
+
+		new PredictionByPartialMatch(markovOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_6() throws Exception {
+		int markovOrder = 2;
+		Random r = null;
+		double probEscape = 0.2;
+
+		new PredictionByPartialMatch(markovOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_7() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.0;
+
+		new PredictionByPartialMatch(markovOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_8() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 1.0;
+
+		new PredictionByPartialMatch(markovOrder, r, probEscape);
+	}
+	
+	@Test
+	public void testPredictionByPartialMatch_9() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 1;
+
+		PredictionByPartialMatch result = new PredictionByPartialMatch(
+				markovOrder, minOrder, r, probEscape);
+
+		assertNotNull(result);
+		assertEquals(markovOrder+1, result.trieOrder);
+		assertEquals(minOrder, result.minOrder);
+		assertEquals(r, result.r);
+		assertEquals(probEscape, result.probEscape, 0.0001);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_10() throws Exception {
+		int markovOrder = -1;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 1;
+
+		new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_11() throws Exception {
+		int markovOrder = 2;
+		Random r = null;
+		double probEscape = 0.2;
+		int minOrder = 1;
+
+		new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_12() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.0;
+		int minOrder = 1;
+
+		new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_13() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 1.0;
+		int minOrder = 1;
+
+		new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_14() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 3;
+
+		new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPredictionByPartialMatch_15() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = -1;
+
+		new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+	}
+
+	@Test
+	public void testGetProbEscape_1() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 1;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+		fixture.probEscape = probEscape;
+		
+		double result = fixture.getProbEscape();
+
+		assertEquals(probEscape, result, 0.0001);
+	}
+
+	@Test
+	public void testGetProbability_1() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 1;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+		fixture.train(sequences);
+		
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("b"));
+		
+		double result = fixture.getProbability(context, symbol);
+		
+		assertEquals(0.88d, result, 0.0001);
+	}
+	
+	@Test
+	public void testGetProbability_2() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 1;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+		fixture.train(sequences);
+		
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("c"));
+		
+		double result = fixture.getProbability(context, symbol);
+		
+		assertEquals(0.04d, result, 0.0001);
+	}
+	
+	@Test
+	public void testGetProbability_3() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 2;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+		fixture.train(sequences);
+		
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("b"));
+		
+		double result = fixture.getProbability(context, symbol);
+		
+		assertEquals(1.0d, result, 0.0001);
+	}
+	
+	@Test
+	public void testGetProbability_4() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 2;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+		fixture.train(sequences);
+		
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("c"));
+		
+		double result = fixture.getProbability(context, symbol);
+		
+		assertEquals(0.0d, result, 0.0001);
+	}
+	
+	@Test
+	public void testGetProbability_5() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 0;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+		fixture.train(sequences);
+		
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("b"));
+		
+		double result = fixture.getProbability(context, symbol);
+		
+		assertEquals(0.8701d, result, 0.0001);
+	}
+	
+	@Test
+	public void testGetProbability_6() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 0;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+		fixture.train(sequences);
+		
+		List<Event> context = new ArrayList<Event>();
+		context.add(Event.STARTEVENT);
+		context.add(new Event(new StringEventType("a")));
+
+		Event symbol = new Event(new StringEventType("c"));
+		
+		double result = fixture.getProbability(context, symbol);
+		
+		assertEquals(0.0350, result, 0.0001);
+	}
+
+	@Test
+	public void testSetProbEscape_1() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+		double probEscape = 0.2;
+		int minOrder = 1;
+		double newProbEscape = 0.3;
+
+		PredictionByPartialMatch fixture = new PredictionByPartialMatch(markovOrder, minOrder, r, probEscape);
+				
+		fixture.setProbEscape(newProbEscape);
+
+		assertEquals(newProbEscape, fixture.probEscape, 0.0001);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		List<Event> sequence = new ArrayList<Event>();
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("c")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("d")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+
+		sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore()
+				.run(PredictionByPartialMatchTest.class);
+	}
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModelTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModelTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModelTest.java	(revision 922)
@@ -0,0 +1,637 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+import de.ugoe.cs.autoquest.usageprofiles.MockTrieBasedModel;
+import de.ugoe.cs.autoquest.usageprofiles.Trie;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+import de.ugoe.cs.autoquest.usageprofiles.TrieNode;
+
+import org.junit.*;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>TrieBasedModelTest</code> contains tests for the class
+ * <code>{@link TrieBasedModel}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class TrieBasedModelTest {
+
+	List<Event> sequence;
+	Collection<Event> symbols;
+
+	private void assertTrieStructure(Trie<Event> trie, int numSequences) {
+		TrieNode<Event> root = trie.find(null);
+		TrieNode<Event> root_a = root.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_a_a = root_a.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_a_b = root_a.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_a_b_a = root_a_b
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_a_b_b = root_a_b
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_a_b_c = root_a_b
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_a_b_d = root_a_b
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_a_b_r = root_a_b
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_a_c = root_a.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_a_c_a = root_a_c
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_a_c_b = root_a_c
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_a_c_c = root_a_c
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_a_c_d = root_a_c
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_a_c_r = root_a_c
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_a_d = root_a.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_a_d_a = root_a_d
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_a_d_b = root_a_d
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_a_d_c = root_a_d
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_a_d_d = root_a_d
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_a_d_r = root_a_d
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_a_r = root_a.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_b = root.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_b_a = root_b.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_b_b = root_b.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_b_c = root_b.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_b_d = root_b.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_b_r = root_b.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_b_r_a = root_b_r
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_b_r_b = root_b_r
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_b_r_c = root_b_r
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_b_r_d = root_b_r
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_b_r_r = root_b_r
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_c = root.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_c_a = root_c.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_c_a_a = root_c_a
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_c_a_b = root_c_a
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_c_a_c = root_c_a
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_c_a_d = root_c_a
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_c_a_r = root_c_a
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_c_b = root_c.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_c_c = root_c.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_c_d = root_c.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_c_r = root_c.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_d = root.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_d_a = root_d.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_d_a_a = root_d_a
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_d_a_b = root_d_a
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_d_a_c = root_d_a
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_d_a_d = root_d_a
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_d_a_r = root_d_a
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_d_b = root_d.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_d_c = root_d.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_d_d = root_d.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_d_r = root_d.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_r = root.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_r_a = root_r.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_r_a_a = root_r_a
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_r_a_b = root_r_a
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_r_a_c = root_r_a
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_r_a_d = root_r_a
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_r_a_r = root_r_a
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_r_a_end = root_r_a.getChild(Event.ENDEVENT);
+		TrieNode<Event> root_r_b = root_r.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_r_c = root_r.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_r_d = root_r.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_r_r = root_r.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_start = root.getChild(Event.STARTEVENT);
+		TrieNode<Event> root_start_a = root_start
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_start_a_a = root_start_a
+				.getChild(new Event(new StringEventType("a")));
+		TrieNode<Event> root_start_a_b = root_start_a
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_start_a_c = root_start_a
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_start_a_d = root_start_a
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_start_a_r = root_start_a
+				.getChild(new Event(new StringEventType("r")));
+		TrieNode<Event> root_start_b = root_start
+				.getChild(new Event(new StringEventType("b")));
+		TrieNode<Event> root_start_c = root_start
+				.getChild(new Event(new StringEventType("c")));
+		TrieNode<Event> root_start_d = root_start
+				.getChild(new Event(new StringEventType("d")));
+		TrieNode<Event> root_start_r = root_start
+				.getChild(new Event(new StringEventType("r")));
+
+		assertEquals(numSequences * 5, root_a.getCount());
+		assertNull(root_a_a);
+		assertEquals(numSequences * 2, root_a_b.getCount());
+		assertNull(root_a_b_a);
+		assertNull(root_a_b_b);
+		assertNull(root_a_b_c);
+		assertNull(root_a_b_d);
+		assertEquals(numSequences * 2, root_a_b_r.getCount());
+		assertEquals(numSequences * 1, root_a_c.getCount());
+		assertEquals(numSequences * 1, root_a_c_a.getCount());
+		assertNull(root_a_c_b);
+		assertNull(root_a_c_c);
+		assertNull(root_a_c_d);
+		assertNull(root_a_c_r);
+		assertEquals(numSequences * 1, root_a_d.getCount());
+		assertEquals(numSequences * 1, root_a_d_a.getCount());
+		assertNull(root_a_d_b);
+		assertNull(root_a_d_c);
+		assertNull(root_a_d_d);
+		assertNull(root_a_d_r);
+		assertNull(root_a_r);
+
+		assertEquals(numSequences * 2, root_b.getCount());
+		assertNull(root_b_a);
+		assertNull(root_b_b);
+		assertNull(root_b_c);
+		assertNull(root_b_d);
+		assertEquals(numSequences * 2, root_b_r.getCount());
+		assertEquals(numSequences * 2, root_b_r_a.getCount());
+		assertNull(root_b_r_b);
+		assertNull(root_b_r_c);
+		assertNull(root_b_r_d);
+		assertNull(root_b_r_r);
+
+		assertEquals(numSequences * 1, root_c.getCount());
+		assertEquals(numSequences * 1, root_c_a.getCount());
+		assertNull(root_c_a_a);
+		assertNull(root_c_a_b);
+		assertNull(root_c_a_c);
+		assertEquals(numSequences * 1, root_c_a_d.getCount());
+		assertNull(root_c_a_r);
+		assertNull(root_c_b);
+		assertNull(root_c_c);
+		assertNull(root_c_d);
+		assertNull(root_c_r);
+
+		assertEquals(numSequences * 1, root_d.getCount());
+		assertEquals(numSequences * 1, root_d_a.getCount());
+		assertNull(root_d_a_a);
+		assertEquals(numSequences * 1, root_d_a_b.getCount());
+		assertNull(root_d_a_c);
+		assertNull(root_d_a_d);
+		assertNull(root_d_a_r);
+		assertNull(root_d_b);
+		assertNull(root_d_c);
+		assertNull(root_d_d);
+		assertNull(root_d_r);
+
+		assertEquals(numSequences * 2, root_r.getCount());
+		assertEquals(numSequences * 2, root_r_a.getCount());
+		assertNull(root_r_a_a);
+		assertNull(root_r_a_b);
+		assertEquals(numSequences * 1, root_r_a_c.getCount());
+		assertNull(root_r_a_d);
+		assertNull(root_r_a_r);
+		assertEquals(numSequences * 1, root_r_a_end.getCount());
+		assertNull(root_r_b);
+		assertNull(root_r_c);
+		assertNull(root_r_d);
+		assertNull(root_r_r);
+
+		assertEquals(numSequences * 1, root_start.getCount());
+		assertEquals(numSequences * 1, root_start_a.getCount());
+		assertNull(root_start_a_a);
+		assertEquals(numSequences * 1, root_start_a_b.getCount());
+		assertNull(root_start_a_c);
+		assertNull(root_start_a_d);
+		assertNull(root_start_a_r);
+		assertNull(root_start_b);
+		assertNull(root_start_c);
+		assertNull(root_start_d);
+		assertNull(root_start_r);
+
+		// check if leafs are really leafs
+		assertTrue(root_a_b_r.isLeaf());
+		assertTrue(root_a_c_a.isLeaf());
+		assertTrue(root_a_d_a.isLeaf());
+		assertTrue(root_b_r_a.isLeaf());
+		assertTrue(root_c_a_d.isLeaf());
+		assertTrue(root_d_a_b.isLeaf());
+		assertTrue(root_r_a_c.isLeaf());
+		assertTrue(root_r_a_end.isLeaf());
+	}
+
+	private static void assertCollectionContent(Collection<?> c1,
+			Collection<?> c2) {
+		assertEquals(c1.size(), c2.size());
+		for (Object obj : c1) {
+			assertTrue(c2.contains(obj));
+		}
+	}
+
+	@Test
+	public void testTrieBasedModel_1() throws Exception {
+		int markovOrder = 2;
+		Random r = new Random();
+
+		MockTrieBasedModel result = new MockTrieBasedModel(markovOrder, r);
+
+		assertNotNull(result);
+		assertEquals(markovOrder + 1, result.trieOrder);
+		assertEquals(r, result.r);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testTrieBasedModel_2() throws Exception {
+		int markovOrder = -1;
+		Random r = new Random();
+
+		new MockTrieBasedModel(markovOrder, r);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testTrieBasedModel_3() throws Exception {
+		int markovOrder = 2;
+		Random r = null;
+
+		new MockTrieBasedModel(markovOrder, r);
+	}
+
+	@Test
+	public void testGenerateSequences_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+		int length = 2;
+
+		Collection<List<Event>> expected = new HashSet<List<Event>>();
+		ArrayList<Event> list;
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("a")));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("b")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("c")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("d")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("b")));
+		list.add(new Event(new StringEventType("r")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("c")));
+		list.add(new Event(new StringEventType("a")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("d")));
+		list.add(new Event(new StringEventType("a")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(new Event(new StringEventType("r")));
+		list.add(new Event(new StringEventType("a")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event(new StringEventType("a")));
+		expected.add(list);
+
+		Collection<List<Event>> result = fixture
+				.generateSequences(length);
+
+		assertCollectionContent(expected, result);
+	}
+
+	@Test
+	public void testGenerateSequences_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+		int length = 3;
+
+		Collection<List<Event>> expected = new HashSet<List<Event>>();
+		ArrayList<Event> list;
+		list = new ArrayList<Event>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event(new StringEventType("a")));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("b")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("c")));
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("d")));
+		expected.add(list);
+
+		Collection<List<Event>> result = fixture
+				.generateSequences(length, true);
+
+		assertCollectionContent(expected, result);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testGenerateSequences_3() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+		int length = 0;
+
+		fixture.generateSequences(length, false);
+	}
+
+	@Test
+	public void testGenerateValidSequences_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+		int length = 5;
+
+		Collection<List<Event>> expected = new HashSet<List<Event>>();
+		ArrayList<Event> list;
+		list = new ArrayList<Event>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("c")));
+		list.add(new Event(new StringEventType("a")));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+		list = new ArrayList<Event>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event(new StringEventType("a")));
+		list.add(new Event(new StringEventType("d")));
+		list.add(new Event(new StringEventType("a")));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+
+		Collection<List<Event>> result = fixture
+				.generateValidSequences(length);
+
+		assertCollectionContent(expected, result);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testGenerateValidSequences_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+		int length = 0;
+
+		fixture.generateValidSequences(length);
+	}
+
+	@Test
+	public void testGetEvents_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+
+		fixture.train(sequences);
+
+		Collection<Event> result = fixture.getEvents();
+
+		assertCollectionContent(symbols, result);
+	}
+
+	@Test
+	public void testGetEvents_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+
+		Collection<Event> result = fixture.getEvents();
+
+		assertCollectionContent(new HashSet<Event>(), result);
+	}
+
+	@Test
+	public void testGetNumFOMStates_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+
+		fixture.train(sequences);
+
+		int result = fixture.getNumFOMStates();
+
+		assertEquals(10, result);
+	}
+
+	@Test
+	public void testGetNumFOMStates_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		;
+
+		int result = fixture.getNumFOMStates();
+
+		assertEquals(0, result);
+	}
+
+	@Test
+	public void testGetNumSymbols_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+
+		int result = fixture.getNumSymbols();
+
+		assertEquals(7, result);
+	}
+
+	@Test
+	public void testGetNumSymbols_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+
+		int result = fixture.getNumSymbols();
+
+		assertEquals(0, result);
+	}
+
+	@Test
+	public void testGetNumTransitions_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+
+		int result = fixture.getNumTransitions();
+
+		assertEquals(11, result);
+	}
+
+	@Test
+	public void testGetNumTransitions_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+
+		int result = fixture.getNumTransitions();
+
+		assertEquals(0, result);
+	}
+
+	@Test
+	public void testTrain_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+
+		fixture.train(sequences);
+
+		assertCollectionContent(symbols, fixture.getEvents());
+
+		assertTrieStructure(fixture.trie, 1);
+	}
+
+	@Test
+	public void testTrain_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		sequences.add(sequence);
+
+		fixture.train(sequences);
+
+		assertCollectionContent(symbols, fixture.getEvents());
+
+		assertTrieStructure(fixture.trie, 2);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testTrain_3() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = null;
+
+		fixture.train(sequences);
+	}
+
+	@Test
+	public void testUpdate_1() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = new ArrayList<List<Event>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+
+		fixture.update(sequences);
+
+		assertCollectionContent(symbols, fixture.getEvents());
+		assertTrieStructure(fixture.trie, 2);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testUpdate_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<Event>> sequences = null;
+		fixture.trie = null;
+
+		fixture.update(sequences);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		sequence = new ArrayList<Event>();
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("c")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("d")));
+		sequence.add(new Event(new StringEventType("a")));
+		sequence.add(new Event(new StringEventType("b")));
+		sequence.add(new Event(new StringEventType("r")));
+		sequence.add(new Event(new StringEventType("a")));
+
+		symbols = new HashSet<Event>();
+		symbols.add(new Event(new StringEventType("a")));
+		symbols.add(new Event(new StringEventType("b")));
+		symbols.add(new Event(new StringEventType("c")));
+		symbols.add(new Event(new StringEventType("d")));
+		symbols.add(new Event(new StringEventType("r")));
+		symbols.add(Event.STARTEVENT);
+		symbols.add(Event.ENDEVENT);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(TrieBasedModelTest.class);
+	}
+}
Index: trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/TrieTest.java
===================================================================
--- trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/TrieTest.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles-test/src/test/java/de/ugoe/cs/autoquest/usageprofiles/TrieTest.java	(revision 922)
@@ -0,0 +1,727 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import junitx.framework.ListAssert;
+
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.usageprofiles.Trie;
+import de.ugoe.cs.autoquest.usageprofiles.TrieNode;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.Edge;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.TrieVertex;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>TrieTest</code> contains tests for the class
+ * <code>{@link Trie}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class TrieTest {
+
+	List<String> sequence;
+	Collection<String> symbols;
+
+	private static void assertCollectionContent(Collection<?> c1,
+			Collection<?> c2) {
+		assertEquals(c1.size(), c2.size());
+		for (Object obj : c1) {
+			assertTrue(c2.contains(obj));
+		}
+	}
+
+	@Test
+	public void testTrie_1() throws Exception {
+
+		Trie<String> result = new Trie<String>();
+
+		assertNotNull(result);
+		assertEquals(0, result.getNumLeafs());
+		assertEquals(0, result.getNumSymbols());
+		assertEquals(0, result.getNumLeafAncestors());
+		assertTrue(result.getKnownSymbols().isEmpty());
+	}
+
+	@Test
+	public void testTrie_2() throws Exception {
+		Trie<String> trie1 = new Trie<String>();
+		trie1.train(sequence, 3);
+
+		Trie<String> result = new Trie<String>(trie1);
+
+		assertEquals(trie1, result);
+		assertNotSame(trie1, result);
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testTrie_3() throws Exception {
+		new Trie<String>(null);
+	}
+
+	@Test
+	public void testAdd_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		List<String> seq = new ArrayList<String>();
+		seq.add("a");
+		seq.add("b");
+
+		fixture.add(seq);
+
+		assertEquals(1, fixture.getChild("a").getCount());
+		assertEquals(1, fixture.getChild("a").getChild("b").getCount());
+		assertNull(fixture.getChild("b"));
+	}
+
+	@Test
+	public void testAdd_2() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+
+		fixture.add(new ArrayList<String>());
+
+		assertEquals(0, fixture.getNumSymbols());
+	}
+
+	@Test
+	public void testAdd_3() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+
+		fixture.add(null);
+
+		assertEquals(0, fixture.getNumSymbols());
+	}
+
+	@Test
+	public void testFind_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> findSequence = new ArrayList<String>();
+		findSequence.add("a");
+		findSequence.add("b");
+		findSequence.add("r");
+		TrieNode<String> expected = fixture.getChild("a").getChild("b")
+				.getChild("r");
+
+		TrieNode<String> result = fixture.find(findSequence);
+
+		assertEquals(expected, result);
+	}
+
+	@Test
+	public void testFind_2() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> findSequence = new ArrayList<String>();
+		findSequence.add("c");
+		findSequence.add("a");
+		TrieNode<String> expected = fixture.getChild("c").getChild("a");
+
+		TrieNode<String> result = fixture.find(findSequence);
+
+		assertEquals(expected, result);
+	}
+
+	@Test
+	public void testFind_3() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> findSequence = new ArrayList<String>();
+
+		TrieNode<String> result = fixture.find(findSequence);
+
+		assertTrue(result.isRoot());
+	}
+
+	@Test
+	public void testFind_4() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+
+		TrieNode<String> result = fixture.find(null);
+
+		assertTrue(result.isRoot());
+	}
+
+	@Test
+	public void testGetChildCreate_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		String symbol = "a";
+
+		TrieNode<String> result = fixture.getChildCreate(symbol);
+
+		assertEquals(symbol, result.getSymbol());
+		assertEquals(0, result.getCount());
+		assertTrue(result.isLeaf());
+	}
+
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testGetChildCreate_2() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.getChildCreate(null);
+	}
+
+	@Test
+	public void testGetContextSuffix_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> context = new ArrayList<String>();
+		context.add("a");
+		context.add("a");
+		context.add("b");
+		List<String> expected = new ArrayList<String>();
+		expected.add("a");
+		expected.add("b");
+
+		List<String> result = fixture.getContextSuffix(context);
+
+		ListAssert.assertEquals(expected, result);
+	}
+
+	@Test
+	public void testGetContextSuffix_2() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> context = new ArrayList<String>();
+		context.add("a");
+		context.add("a");
+		context.add("b");
+		context.add("r");
+		List<String> expected = new ArrayList<String>();
+		expected.add("b");
+		expected.add("r");
+
+		List<String> result = fixture.getContextSuffix(context);
+
+		ListAssert.assertEquals(expected, result);
+	}
+
+	@Test
+	public void testGetContextSuffix_3() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> context = new ArrayList<String>();
+		context.add("a");
+		context.add("a");
+		context.add("b");
+		context.add("x");
+		List<String> expected = new ArrayList<String>();
+
+		List<String> result = fixture.getContextSuffix(context);
+
+		ListAssert.assertEquals(expected, result);
+	}
+
+	@Test
+	public void testGetContextSuffix_4() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+
+		List<String> result = fixture.getContextSuffix(null);
+
+		// add additional test code here
+		assertNotNull(result);
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetContextSuffix_5() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> context = new ArrayList<String>();
+		context.add("a");
+		context.add("a");
+		context.add("b");
+		List<String> expected = new ArrayList<String>();
+		expected.add("a");
+		expected.add("b");
+
+		List<String> result = fixture.getContextSuffix(context);
+
+		ListAssert.assertEquals(expected, result);
+	}
+
+	@Test
+	public void testGetCount_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+		subSequence.add("a");
+
+		int result = fixture.getCount(subSequence);
+
+		assertEquals(5, result);
+	}
+
+	@Test
+	public void testGetCount_2() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+		subSequence.add("a");
+		subSequence.add("b");
+
+		int result = fixture.getCount(subSequence);
+
+		assertEquals(2, result);
+	}
+
+	@Test
+	public void testGetCount_3() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+		subSequence.add("x");
+
+		int result = fixture.getCount(subSequence);
+
+		assertEquals(0, result);
+	}
+
+	@Test
+	public void testGetCount_4() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+
+		int result = fixture.getCount(subSequence, "a");
+
+		assertEquals(5, result);
+	}
+
+	@Test
+	public void testGetCount_5() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+		subSequence.add("a");
+		subSequence.add("b");
+
+		int result = fixture.getCount(subSequence, "r");
+
+		assertEquals(2, result);
+	}
+
+	@Test
+	public void testGetCount_6() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+
+		int result = fixture.getCount(subSequence, "x");
+
+		assertEquals(0, result);
+	}
+
+	@Test
+	public void testGetFollowingSymbols_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+		subSequence.add("a");
+		Collection<String> expected = new ArrayList<String>();
+		expected.add("b");
+		expected.add("c");
+		expected.add("d");
+
+		Collection<String> result = fixture.getFollowingSymbols(subSequence);
+
+		assertCollectionContent(expected, result);
+	}
+
+	@Test
+	public void testGetFollowingSymbols_2() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+		subSequence.add("a");
+		subSequence.add("b");
+		subSequence.add("r");
+
+		Collection<String> result = fixture.getFollowingSymbols(subSequence);
+
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetFollowingSymbols_3() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+		List<String> subSequence = new ArrayList<String>();
+		subSequence.add("x");
+
+		Collection<String> result = fixture.getFollowingSymbols(subSequence);
+
+		assertEquals(0, result.size());
+	}
+
+	@Test
+	public void testGetNumLeafAncestors_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+
+		int result = fixture.getNumLeafAncestors();
+
+		assertEquals(7, result);
+	}
+
+	@Test
+	public void testGetNumLeafs_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+
+		int result = fixture.getNumLeafs();
+
+		assertEquals(7, result);
+	}
+
+	@Test
+	public void testGetNumSymbols_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+
+		int result = fixture.getNumSymbols();
+
+		assertEquals(5, result);
+	}
+
+	@Test
+	public void testTrain_1() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		int maxOrder = 3;
+
+		fixture.train(sequence, maxOrder);
+
+		// check if symbols are correct
+		assertCollectionContent(symbols, fixture.getKnownSymbols());
+
+		// check if counters are correct and only the correct nodes exist
+		TrieNode<String> root = fixture.find(new ArrayList<String>());
+		TrieNode<String> root_a = root.getChild("a");
+		TrieNode<String> root_a_a = root_a.getChild("a");
+		TrieNode<String> root_a_b = root_a.getChild("b");
+		TrieNode<String> root_a_b_a = root_a_b.getChild("a");
+		TrieNode<String> root_a_b_b = root_a_b.getChild("b");
+		TrieNode<String> root_a_b_c = root_a_b.getChild("c");
+		TrieNode<String> root_a_b_d = root_a_b.getChild("d");
+		TrieNode<String> root_a_b_r = root_a_b.getChild("r");
+		TrieNode<String> root_a_c = root_a.getChild("c");
+		TrieNode<String> root_a_c_a = root_a_c.getChild("a");
+		TrieNode<String> root_a_c_b = root_a_c.getChild("b");
+		TrieNode<String> root_a_c_c = root_a_c.getChild("c");
+		TrieNode<String> root_a_c_d = root_a_c.getChild("d");
+		TrieNode<String> root_a_c_r = root_a_c.getChild("r");
+		TrieNode<String> root_a_d = root_a.getChild("d");
+		TrieNode<String> root_a_d_a = root_a_d.getChild("a");
+		TrieNode<String> root_a_d_b = root_a_d.getChild("b");
+		TrieNode<String> root_a_d_c = root_a_d.getChild("c");
+		TrieNode<String> root_a_d_d = root_a_d.getChild("d");
+		TrieNode<String> root_a_d_r = root_a_d.getChild("r");
+		TrieNode<String> root_a_r = root_a.getChild("r");
+		TrieNode<String> root_b = root.getChild("b");
+		TrieNode<String> root_b_a = root_b.getChild("a");
+		TrieNode<String> root_b_b = root_b.getChild("b");
+		TrieNode<String> root_b_c = root_b.getChild("c");
+		TrieNode<String> root_b_d = root_b.getChild("d");
+		TrieNode<String> root_b_r = root_b.getChild("r");
+		TrieNode<String> root_b_r_a = root_b_r.getChild("a");
+		TrieNode<String> root_b_r_b = root_b_r.getChild("b");
+		TrieNode<String> root_b_r_c = root_b_r.getChild("c");
+		TrieNode<String> root_b_r_d = root_b_r.getChild("d");
+		TrieNode<String> root_b_r_r = root_b_r.getChild("r");
+		TrieNode<String> root_c = root.getChild("c");
+		TrieNode<String> root_c_a = root_c.getChild("a");
+		TrieNode<String> root_c_a_a = root_c_a.getChild("a");
+		TrieNode<String> root_c_a_b = root_c_a.getChild("b");
+		TrieNode<String> root_c_a_c = root_c_a.getChild("c");
+		TrieNode<String> root_c_a_d = root_c_a.getChild("d");
+		TrieNode<String> root_c_a_r = root_c_a.getChild("r");
+		TrieNode<String> root_c_b = root_c.getChild("b");
+		TrieNode<String> root_c_c = root_c.getChild("c");
+		TrieNode<String> root_c_d = root_c.getChild("d");
+		TrieNode<String> root_c_r = root_c.getChild("r");
+		TrieNode<String> root_d = root.getChild("d");
+		TrieNode<String> root_d_a = root_d.getChild("a");
+		TrieNode<String> root_d_a_a = root_d_a.getChild("a");
+		TrieNode<String> root_d_a_b = root_d_a.getChild("b");
+		TrieNode<String> root_d_a_c = root_d_a.getChild("c");
+		TrieNode<String> root_d_a_d = root_d_a.getChild("d");
+		TrieNode<String> root_d_a_r = root_d_a.getChild("r");
+		TrieNode<String> root_d_b = root_d.getChild("b");
+		TrieNode<String> root_d_c = root_d.getChild("c");
+		TrieNode<String> root_d_d = root_d.getChild("d");
+		TrieNode<String> root_d_r = root_d.getChild("r");
+		TrieNode<String> root_r = root.getChild("r");
+		TrieNode<String> root_r_a = root_r.getChild("a");
+		TrieNode<String> root_r_a_a = root_r_a.getChild("a");
+		TrieNode<String> root_r_a_b = root_r_a.getChild("b");
+		TrieNode<String> root_r_a_c = root_r_a.getChild("c");
+		TrieNode<String> root_r_a_d = root_r_a.getChild("d");
+		TrieNode<String> root_r_a_r = root_r_a.getChild("r");
+		TrieNode<String> root_r_b = root_r.getChild("b");
+		TrieNode<String> root_r_c = root_r.getChild("c");
+		TrieNode<String> root_r_d = root_r.getChild("d");
+		TrieNode<String> root_r_r = root_r.getChild("r");
+
+		assertEquals(5, root_a.getCount());
+		assertNull(root_a_a);
+		assertEquals(2, root_a_b.getCount());
+		assertNull(root_a_b_a);
+		assertNull(root_a_b_b);
+		assertNull(root_a_b_c);
+		assertNull(root_a_b_d);
+		assertEquals(2, root_a_b_r.getCount());
+		assertEquals(1, root_a_c.getCount());
+		assertEquals(1, root_a_c_a.getCount());
+		assertNull(root_a_c_b);
+		assertNull(root_a_c_c);
+		assertNull(root_a_c_d);
+		assertNull(root_a_c_r);
+		assertEquals(1, root_a_d.getCount());
+		assertEquals(1, root_a_d_a.getCount());
+		assertNull(root_a_d_b);
+		assertNull(root_a_d_c);
+		assertNull(root_a_d_d);
+		assertNull(root_a_d_r);
+		assertNull(root_a_r);
+
+		assertEquals(2, root_b.getCount());
+		assertNull(root_b_a);
+		assertNull(root_b_b);
+		assertNull(root_b_c);
+		assertNull(root_b_d);
+		assertEquals(2, root_b_r.getCount());
+		assertEquals(2, root_b_r_a.getCount());
+		assertNull(root_b_r_b);
+		assertNull(root_b_r_c);
+		assertNull(root_b_r_d);
+		assertNull(root_b_r_r);
+
+		assertEquals(1, root_c.getCount());
+		assertEquals(1, root_c_a.getCount());
+		assertNull(root_c_a_a);
+		assertNull(root_c_a_b);
+		assertNull(root_c_a_c);
+		assertEquals(1, root_c_a_d.getCount());
+		assertNull(root_c_a_r);
+		assertNull(root_c_b);
+		assertNull(root_c_c);
+		assertNull(root_c_d);
+		assertNull(root_c_r);
+
+		assertEquals(1, root_d.getCount());
+		assertEquals(1, root_d_a.getCount());
+		assertNull(root_d_a_a);
+		assertEquals(1, root_d_a_b.getCount());
+		assertNull(root_d_a_c);
+		assertNull(root_d_a_d);
+		assertNull(root_d_a_r);
+		assertNull(root_d_b);
+		assertNull(root_d_c);
+		assertNull(root_d_d);
+		assertNull(root_d_r);
+
+		assertEquals(2, root_r.getCount());
+		assertEquals(2, root_r_a.getCount());
+		assertNull(root_r_a_a);
+		assertNull(root_r_a_b);
+		assertEquals(1, root_r_a_c.getCount());
+		assertNull(root_r_a_d);
+		assertNull(root_r_a_r);
+		assertNull(root_r_b);
+		assertNull(root_r_c);
+		assertNull(root_r_d);
+		assertNull(root_r_r);
+
+		// check if leafs are really leafs
+		assertTrue(root_a_b_r.isLeaf());
+		assertTrue(root_a_c_a.isLeaf());
+		assertTrue(root_a_d_a.isLeaf());
+		assertTrue(root_b_r_a.isLeaf());
+		assertTrue(root_c_a_d.isLeaf());
+		assertTrue(root_d_a_b.isLeaf());
+		assertTrue(root_r_a_c.isLeaf());
+	}
+
+	@Test
+	public void testTrain_2() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		int maxOrder = 0;
+
+		fixture.train(sequence, maxOrder);
+
+		assertTrue(fixture.getKnownSymbols().isEmpty());
+	}
+
+	@Test
+	public void testTrain_3() throws Exception {
+		Trie<Object> fixture = new Trie<Object>();
+		List<Object> sequence = new ArrayList<Object>();
+		int maxOrder = 1;
+
+		fixture.train(sequence, maxOrder);
+
+		assertTrue(fixture.getKnownSymbols().isEmpty());
+	}
+
+	@Test
+	public void testTrain_4() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		List<String> sequence = new ArrayList<String>();
+		sequence.add("a");
+		sequence.add("b");
+		int maxOrder = 3;
+
+		fixture.train(sequence, maxOrder);
+
+		assertCollectionContent(sequence, fixture.getKnownSymbols());
+		TrieNode<String> root = fixture.find(new ArrayList<String>());
+		TrieNode<String> root_a = root.getChild("a");
+		TrieNode<String> root_a_a = root_a.getChild("a");
+		TrieNode<String> root_a_b = root_a.getChild("b");
+		TrieNode<String> root_b = root.getChild("b");
+		TrieNode<String> root_b_a = root_b.getChild("a");
+		TrieNode<String> root_b_b = root_b.getChild("b");
+
+		assertEquals(1, root_a.getCount());
+		assertNull(root_a_a);
+		assertEquals(1, root_a_b.getCount());
+		assertEquals(1, root_b.getCount());
+		assertNull(root_b_a);
+		assertNull(root_b_b);
+
+		assertTrue(root_a_b.isLeaf());
+		assertTrue(root_b.isLeaf());
+	}
+
+	@Test
+	public void testEdgeEdge_1() throws Exception {
+		Edge result = new Edge();
+
+		assertNotNull(result);
+	}
+
+	@Test
+	public void testTrieVertexTrieVertex_1() throws Exception {
+		String id = "idString";
+
+		TrieVertex result = new TrieVertex(id);
+
+		assertNotNull(result);
+	}
+
+	@Test
+	public void testTrieVertexToString_1() throws Exception {
+		String id = "idString";
+		TrieVertex fixture = new TrieVertex(id);
+
+		String result = fixture.toString();
+
+		assertEquals(id, result);
+	}
+
+	@Test
+	public void testEquals_1() throws Exception {
+		Trie<String> trieOther = new Trie<String>();
+		Trie<String> fixture = new Trie<String>();
+
+		boolean result = fixture.equals(trieOther);
+
+		assertEquals(true, result);
+	}
+
+	@Test
+	public void testEquals_2() throws Exception {
+		Trie<String> trieOther = new Trie<String>();
+		trieOther.train(sequence, 2);
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 2);
+
+		boolean result = fixture.equals(trieOther);
+
+		assertEquals(true, result);
+	}
+
+	@Test
+	public void testEquals_3() throws Exception {
+		Trie<String> trieOther = new Trie<String>();
+		trieOther.train(sequence, 2);
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 3);
+
+		boolean result = fixture.equals(trieOther);
+
+		assertEquals(false, result);
+	}
+
+	@Test
+	public void testEquals_4() throws Exception {
+		Trie<String> trieOther = new Trie<String>();
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 2);
+
+		boolean result = fixture.equals(trieOther);
+
+		assertEquals(false, result);
+	}
+
+	@Test
+	public void testEquals_5() throws Exception {
+		Trie<String> trieOther = new Trie<String>();
+		trieOther.train(sequence, 2);
+		Trie<String> fixture = new Trie<String>();
+
+		boolean result = fixture.equals(trieOther);
+
+		assertEquals(false, result);
+	}
+
+	@Test
+	public void testEquals_6() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 2);
+
+		boolean result = fixture.equals(fixture);
+
+		assertEquals(true, result);
+	}
+
+	@Test
+	public void testEquals_7() throws Exception {
+		Trie<String> fixture = new Trie<String>();
+		fixture.train(sequence, 2);
+
+		boolean result = fixture.equals(null);
+
+		assertEquals(false, result);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		sequence = new ArrayList<String>();
+		sequence.add("a");
+		sequence.add("b");
+		sequence.add("r");
+		sequence.add("a");
+		sequence.add("c");
+		sequence.add("a");
+		sequence.add("d");
+		sequence.add("a");
+		sequence.add("b");
+		sequence.add("r");
+		sequence.add("a");
+
+		symbols = new HashSet<String>();
+		symbols.add("a");
+		symbols.add("b");
+		symbols.add("c");
+		symbols.add("d");
+		symbols.add("r");
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(TrieTest.class);
+	}
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomaton.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomaton.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/DeterministicFiniteAutomaton.java	(revision 922)
@@ -0,0 +1,79 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * Implements a Deterministic Finite Automata (DFA) capable of random session generation. It is a
+ * special case of a first-order Markov model, where the transition probability is equally high for
+ * all following states.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class DeterministicFiniteAutomaton extends FirstOrderMarkovModel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new DeterministicFiniteAutomaton.
+     * </p>
+     * 
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public DeterministicFiniteAutomaton(Random r) {
+        super(r);
+    }
+
+    /**
+     * <p>
+     * Calculates the proability of the next state. Each of the following states in the automaton is
+     * equally probable.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util.List,
+     *      de.ugoe.cs.autoquest.eventcore.Event)
+     */
+    @Override
+    public double getProbability(List<Event> context, Event symbol) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (symbol == null) {
+            throw new IllegalArgumentException("symbol must not be null");
+        }
+        double result = 0.0d;
+
+        List<Event> contextCopy;
+        if (context.size() >= trieOrder) {
+            contextCopy =
+                new LinkedList<Event>(context.subList(context.size() - trieOrder + 1,
+                                                      context.size()));
+        }
+        else {
+            contextCopy = new LinkedList<Event>(context);
+        }
+
+        Collection<Event> followers = trie.getFollowingSymbols(contextCopy);
+
+        if (followers.size() != 0 && followers.contains(symbol)) {
+            result = 1.0d / followers.size();
+        }
+
+        return result;
+    }
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModel.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModel.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/FirstOrderMarkovModel.java	(revision 922)
@@ -0,0 +1,256 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.SparseMultigraph;
+import edu.uci.ics.jung.graph.util.EdgeType;
+
+import Jama.Matrix;
+
+/**
+ * <p>
+ * Implements first-order Markov models. The implementation is based on {@link HighOrderMarkovModel}
+ * and restricts the Markov order to 1. In comparison to {@link HighOrderMarkovModel}, more
+ * calculations are possible with first-order models, e.g., the calculation of the entropy (
+ * {@link #calcEntropy()}).
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class FirstOrderMarkovModel extends HighOrderMarkovModel implements IDotCompatible {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Maximum number of iterations when calculating the stationary distribution as the limit of
+     * multiplying the transmission matrix with itself.
+     * </p>
+     */
+    final static int MAX_STATDIST_ITERATIONS = 1000;
+
+    /**
+     * <p>
+     * Constructor. Creates a new FirstOrderMarkovModel.
+     * </p>
+     * 
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public FirstOrderMarkovModel(Random r) {
+        super(1, r);
+    }
+
+    /**
+     * <p>
+     * Generates the transmission matrix of the Markov model.
+     * </p>
+     * 
+     * @return transmission matrix
+     */
+    private Matrix getTransmissionMatrix() {
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+        int numStates = knownSymbols.size();
+        Matrix transmissionMatrix = new Matrix(numStates, numStates);
+
+        for (int i = 0; i < numStates; i++) {
+            Event currentSymbol = knownSymbols.get(i);
+            List<Event> context = new ArrayList<Event>();
+            context.add(currentSymbol);
+            for (int j = 0; j < numStates; j++) {
+                Event follower = knownSymbols.get(j);
+                double prob = getProbability(context, follower);
+                transmissionMatrix.set(i, j, prob);
+            }
+        }
+        return transmissionMatrix;
+    }
+
+    /**
+     * <p>
+     * Calculates the entropy of the model. To make it possible that the model is stationary, a
+     * transition from {@link Event#ENDEVENT} to {@link Event#STARTEVENT} is added.
+     * </p>
+     * 
+     * @return entropy of the model or NaN if it could not be calculated
+     */
+    public double calcEntropy() {
+        Matrix transmissionMatrix = getTransmissionMatrix();
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+        int numStates = knownSymbols.size();
+
+        List<Integer> startIndexList = new LinkedList<Integer>();
+        List<Integer> endIndexList = new LinkedList<Integer>();
+        for (int i = 0; i < knownSymbols.size(); i++) {
+            String id = knownSymbols.get(i).getId();
+            if (id.equals(Event.STARTEVENT.getId()) ||
+                id.contains(Event.STARTEVENT.getId() + "-=-"))
+            {
+                startIndexList.add(i);
+            }
+            if (id.equals(Event.ENDEVENT.getId()) || id.contains("-=-" + Event.ENDEVENT.getId())) {
+                endIndexList.add(i);
+            }
+        }
+
+        if (startIndexList.isEmpty()) {
+            Console
+                .printerrln("Error calculating entropy. Initial state of markov chain not found.");
+            return Double.NaN;
+        }
+        if (endIndexList.isEmpty()) {
+            Console.printerrln("Error calculating entropy. End state of markov chain not found.");
+            return Double.NaN;
+        }
+        for (Integer i : endIndexList) {
+            for (Integer j : startIndexList) {
+                transmissionMatrix.set(i, j, 1);
+            }
+        }
+
+        // Calculate stationary distribution by raising the power of the
+        // transmission matrix.
+        // The rank of the matrix should fall to 1 and each two should be the
+        // vector of the stationory distribution.
+        int iter = 0;
+        int rank = transmissionMatrix.rank();
+        Matrix stationaryMatrix = (Matrix) transmissionMatrix.clone();
+        while (iter < MAX_STATDIST_ITERATIONS && rank > 1) {
+            stationaryMatrix = stationaryMatrix.times(stationaryMatrix);
+            rank = stationaryMatrix.rank();
+            iter++;
+        }
+
+        if (rank != 1) {
+            Console.traceln(Level.FINE, "rank: " + rank);
+            Console.printerrln("Unable to calculate stationary distribution.");
+            return Double.NaN;
+        }
+
+        double entropy = 0.0;
+        for (int i = 0; i < numStates; i++) {
+            for (int j = 0; j < numStates; j++) {
+                if (transmissionMatrix.get(i, j) != 0 && transmissionMatrix.get(i, j) != 1) {
+                    double tmp = stationaryMatrix.get(0, i);
+                    tmp *= transmissionMatrix.get(i, j);
+                    tmp *= Math.log(transmissionMatrix.get(i, j)) / Math.log(2);
+                    entropy -= tmp;
+                }
+            }
+        }
+        return entropy;
+    }
+
+    /**
+     * <p>
+     * The dot represenation of {@link FirstOrderMarkovModel}s is its graph representation with the
+     * states as nodes and directed edges weighted with transition probabilities.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IDotCompatible#getDotRepresentation()
+     */
+    @Override
+    public String getDotRepresentation() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("digraph model {" + StringTools.ENDLINE);
+
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+        for (Event symbol : knownSymbols) {
+            final String thisSaneId = symbol.getId().replace("\"", "\\\"").replaceAll("[\r\n]", "");
+            stringBuilder.append(" " + knownSymbols.indexOf(symbol) + " [label=\"" + thisSaneId +
+                "\"];" + StringTools.ENDLINE);
+            List<Event> context = new ArrayList<Event>();
+            context.add(symbol);
+            Collection<Event> followers = trie.getFollowingSymbols(context);
+            for (Event follower : followers) {
+                stringBuilder.append(" " + knownSymbols.indexOf(symbol) + " -> " +
+                    knownSymbols.indexOf(follower) + " ");
+                stringBuilder.append("[label=\"" + getProbability(context, follower) + "\"];" +
+                    StringTools.ENDLINE);
+            }
+        }
+        stringBuilder.append('}' + StringTools.ENDLINE);
+        return stringBuilder.toString();
+    }
+
+    /**
+     * <p>
+     * Returns a {@link Graph} representation of the model with the states as nodes and directed
+     * edges weighted with transition probabilities.
+     * </p>
+     * 
+     * @return {@link Graph} of the model
+     */
+    public Graph<String, MarkovEdge> getGraph() {
+        Graph<String, MarkovEdge> graph = new SparseMultigraph<String, MarkovEdge>();
+
+        List<Event> knownSymbols = new ArrayList<Event>(trie.getKnownSymbols());
+
+        for (Event symbol : knownSymbols) {
+            String from = symbol.getId();
+            List<Event> context = new ArrayList<Event>();
+            context.add(symbol);
+
+            Collection<Event> followers = trie.getFollowingSymbols(context);
+
+            for (Event follower : followers) {
+                String to = follower.getId();
+                MarkovEdge prob = new MarkovEdge(getProbability(context, follower));
+                graph.addEdge(prob, from, to, EdgeType.DIRECTED);
+            }
+        }
+        return graph;
+    }
+
+    /**
+     * Inner class used for the {@link Graph} representation of the model.
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    static public class MarkovEdge {
+        /**
+         * <p>
+         * Weight of the edge, i.e., its transition probability.
+         * </p>
+         */
+        double weight;
+
+        /**
+         * <p>
+         * Constructor. Creates a new MarkovEdge.
+         * </p>
+         * 
+         * @param weight
+         *            weight of the edge, i.e., its transition probability
+         */
+        MarkovEdge(double weight) {
+            this.weight = weight;
+        }
+
+        /**
+         * <p>
+         * The weight of the edge as {@link String}.
+         * </p>
+         */
+        public String toString() {
+            return "" + weight;
+        }
+    }
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModel.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModel.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/HighOrderMarkovModel.java	(revision 922)
@@ -0,0 +1,84 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * Implements high-order Markov models.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class HighOrderMarkovModel extends TrieBasedModel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new HighOrderMarkovModel with a defined Markov order.
+     * </p>
+     * 
+     * @param maxOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public HighOrderMarkovModel(int maxOrder, Random r) {
+        super(maxOrder, r);
+    }
+
+    /**
+     * <p>
+     * Calculates the probability of the next Event being symbol based on the order of the Markov
+     * model. The order is defined in the constructor {@link #HighOrderMarkovModel(int, Random)}.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util.List,
+     *      de.ugoe.cs.autoquest.eventcore.Event)
+     */
+    @Override
+    public double getProbability(List<Event> context, Event symbol) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (symbol == null) {
+            throw new IllegalArgumentException("symbol must not be null");
+        }
+        double result = 0.0d;
+
+        List<Event> contextCopy;
+        if (context.size() >= trieOrder) {
+            contextCopy =
+                new LinkedList<Event>(context.subList(context.size() - trieOrder + 1,
+                                                      context.size()));
+        }
+        else {
+            contextCopy = new LinkedList<Event>(context);
+        }
+
+        Collection<Event> followers = trie.getFollowingSymbols(contextCopy);
+        int sumCountFollowers = 0; // N(s\sigma')
+        for (Event follower : followers) {
+            sumCountFollowers += trie.getCount(contextCopy, follower);
+        }
+
+        int countSymbol = trie.getCount(contextCopy, symbol);
+        if (sumCountFollowers != 0) {
+            result = ((double) countSymbol / sumCountFollowers);
+        }
+
+        return result;
+    }
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IDotCompatible.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IDotCompatible.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IDotCompatible.java	(revision 922)
@@ -0,0 +1,22 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+/**
+ * <p>
+ * Models implementing this interface provide a graph representation of themselves as a
+ * {@link String} with Dot syntax.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface IDotCompatible {
+
+    /**
+     * <p>
+     * Returns a Dot representation of the model.
+     * </p>
+     * 
+     * @return string with Dot syntax that describes the model.
+     */
+    public abstract String getDotRepresentation();
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IMemory.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IMemory.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IMemory.java	(revision 922)
@@ -0,0 +1,40 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.List;
+
+/**
+ * <p>
+ * This interface defines basic functions for classes that implement a memory about the recent
+ * events of a sequences.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ * @param <T>
+ *            Type of the sequence elements that are memorized.
+ */
+public interface IMemory<T> {
+
+    /**
+     * Adds an element to the end of the memory.
+     * 
+     * @param element
+     *            Element to be added.
+     */
+    public void add(T element);
+
+    /**
+     * <p>
+     * Returns the last <code>num</code> memorized elements. If the history is shorter than
+     * <code>num</code>, the length of the returned {@link java.util.List} may be less than
+     * <code>num</code>.
+     * </p>
+     * 
+     * @param num
+     *            Number of states from the end of the memory to be returned.
+     * @return {@link java.util.List} of memorized elements.
+     */
+    public List<T> getLast(int num);
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IStochasticProcess.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IStochasticProcess.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IStochasticProcess.java	(revision 922)
@@ -0,0 +1,179 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * This interface defines the functionalities provided by stochastic processes.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface IStochasticProcess extends Serializable {
+
+    /**
+     * <p>
+     * Returns the probability, that the next event is {@code symbol} given the last events are
+     * {@code context}. The last element of {@code context} is the most recent in the history, the
+     * first element is the oldest.
+     * </p>
+     * 
+     * @param context
+     *            recently observed symbols
+     * @param symbol
+     *            event for which the probability is calculated
+     * @return probabilty the {@code symbol} is the next event, given the last events
+     *         {@code context}
+     * @throws IllegalArgumentException
+     *             thrown if context or symbol is null
+     */
+    double getProbability(List<Event> context, Event symbol);
+
+    /**
+     * <p>
+     * Returns the probabilitiy that a given sequence is generated by the stochastic process.
+     * </p>
+     * 
+     * @param sequence
+     *            sequences of which the probability is calculated
+     * @return probability of the sequences; 1.0 if sequence is empty or null
+     * @throws IllegalArgumentException
+     *             thrown if sequence is null
+     */
+    double getProbability(List<Event> sequence);
+
+    /**
+     * <p>
+     * Generates a random sequence of events. The sequence starts with {@link Event#STARTEVENT} and
+     * finishes with {@link Event#ENDEVENT}.
+     * </p>
+     * 
+     * @return randomly generated sequence
+     */
+    public List<Event> randomSequence();
+
+    /**
+     * <p>
+     * Generates a random sequence of events. The sequence starts with {@link Event#STARTEVENT} and
+     * finishes with
+     * <ul>
+     * <li>{@link Event#ENDEVENT} if validEnd==true.</li>
+     * <li>b) if a generated sequences reaches {@link Event#ENDEVENT} before maxLength, the sequence
+     * finishes and is shorter than maxLenght. Otherwise, the sequence finishes as soon as maxLength
+     * is reached and the final event of the sequence must not be {@link Event#ENDEVENT}.</li>
+     * </ul>
+     * </p>
+     * 
+     * @param maxLength
+     *            maximum length of the generated sequence
+     * @param validEnd
+     *            if true, only sequences that finish with {@link Event#ENDEVENT} are generated
+     * @return randomly generated sequence
+     * 
+     */
+    public List<Event> randomSequence(int maxLength, boolean validEnd);
+
+    /**
+     * <p>
+     * Generates all sequences of a given length are possible, i.e., have a positive probability.<br>
+     * All states are used as possible starting states.
+     * </p>
+     * 
+     * @param length
+     *            length of the generated sequences
+     * @return generated sequences
+     * @see #generateSequences(int, boolean)
+     * @throws IllegalArgumentException
+     *             thrown if length is less than or equal to 0
+     */
+    public Collection<List<Event>> generateSequences(int length);
+
+    /**
+     * <p>
+     * Generates all sequences of given length that can are possible, i.e., have positive
+     * probability.<br>
+     * If {@code fromStart==true}, all generated sequences start in {@link Event#STARTEVENT}.
+     * Otherwise this method is the same as {@link #generateSequences(int)}.
+     * </p>
+     * 
+     * @param length
+     *            length of the generated sequences
+     * @param fromStart
+     *            if true, all generated sequences start with {@link Event#STARTEVENT}
+     * @return generated sequences
+     * @throws IllegalArgumentException
+     *             thrown if length is less than or equal to 0
+     */
+    public Collection<List<Event>> generateSequences(int length, boolean fromStart);
+
+    /**
+     * <p>
+     * Generates all sequences starting with {@link Event#STARTEVENT} and finishing with
+     * {@link Event#ENDEVENT} of a given length. It is possible that no such sequence exists with
+     * the defined length and the returned set is empty. If {@code length} is less than 2 the
+     * returned set is always empty.
+     * </p>
+     * 
+     * @param length
+     * @return generated sequences
+     * @throws IllegalArgumentException
+     *             thrown if length is less than or equal to 0
+     */
+    public Collection<List<Event>> generateValidSequences(int length);
+
+    /**
+     * <p>
+     * Returns the number of states known by the stochastic process, i.e., the size of its alphabet.
+     * </p>
+     * 
+     * @return number of states
+     */
+    public int getNumSymbols();
+
+    /**
+     * <p>
+     * Returns a string representation of all known states.
+     * </p>
+     * 
+     * @return string representation for all known states
+     */
+    public String[] getSymbolStrings();
+
+    /**
+     * <p>
+     * Returns the number of states the process would have if it would be flattened through
+     * state-splitting to a first-order Markov model.
+     * </p>
+     * <p>
+     * If it is not possible to flatten the model, -1 is returned.
+     * </p>
+     * 
+     * @return number of states an equivalent FOM would have; -1 if not available
+     */
+    public int getNumFOMStates();
+
+    /**
+     * <p>
+     * Returns the number of transitions the process would have if it would be flattened through
+     * state-splitting to a first-order Markov model.
+     * </p>
+     * 
+     * @return number of transitions an equivalent FOM would have; -1 if not available
+     */
+    public int getNumTransitions();
+
+    /**
+     * <p>
+     * Returns all states known by the stochastic process, i.e., its {@link Event}s.
+     * </p>
+     * 
+     * @return events known by the process
+     */
+    public Collection<Event> getEvents();
+
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemory.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemory.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/IncompleteMemory.java	(revision 922)
@@ -0,0 +1,93 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <p>
+ * Implements a round-trip buffered memory of a specified length that can be used to remember the
+ * recent history. Every event that happend longer ago than the length of the memory is forgotten,
+ * hence the memory is incomplete.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ * @param <T>
+ *            Type which is memorized.
+ */
+public class IncompleteMemory<T> implements IMemory<T> {
+
+    /**
+     * <p>
+     * Maximum length of the memory.
+     * </p>
+     */
+    private int length;
+
+    /**
+     * <p>
+     * Internal storage of the history.
+     * </p>
+     */
+    private List<T> history;
+
+    /**
+     * <p>
+     * Constructor. Creates a new IncompleteMemory.
+     * </p>
+     * 
+     * @param length
+     *            number of recent events that are remembered
+     * @throws IllegalArgumentException
+     *             This exception is thrown if the length is smaller than 1
+     */
+    public IncompleteMemory(int length) {
+        if (length < 1) {
+            throw new IllegalArgumentException("Length of IncompleteMemory must be at least 1.");
+        }
+        this.length = length;
+        history = new LinkedList<T>();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IMemory#add(java.lang.Object)
+     */
+    @Override
+    public void add(T state) {
+        if (history.size() == length) {
+            history.remove(0);
+        }
+        history.add(state);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IMemory#getLast(int)
+     */
+    @Override
+    public List<T> getLast(int num) {
+        if (num < 1) {
+            return new LinkedList<T>();
+        }
+        else {
+            return new LinkedList<T>(history.subList(Math.max(0, history.size() - num),
+                                                     history.size())); // defensive copy
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the current length of the memory. This can be less than {@link #length}, if the
+     * overall history is less than {@link #length}.
+     * </p>
+     * 
+     * @return length of the current memory
+     */
+    public int getLength() {
+        return history.size();
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattener.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattener.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/ModelFlattener.java	(revision 922)
@@ -0,0 +1,131 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.StringEventType;
+
+/**
+ * <p>
+ * This class provides functions to create flattened first-order Markov models from
+ * {@link HighOrderMarkovModel}s and {@link PredictionByPartialMatch} models through state
+ * splitting.
+ * </p>
+ * <p>
+ * If possible, the normal high-order markov model should be used, as the Events may be broken by
+ * the flattener, as, e.g., the information {@link ReplayableEvent}s contain is not preserved.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class ModelFlattener {
+
+    private static final String EVENT_SEPARATOR = "-=-";
+
+    Trie<Event> firstOrderTrie;
+
+    /**
+     * <p>
+     * Takes a {@link HighOrderMarkovModel} and returns a {@link FirstOrderMarkovModel} that
+     * conserves the high-order memory through state splitting.
+     * </p>
+     * 
+     * @param model
+     *            model that is flattened
+     * @return flattened first-order Markov model
+     */
+    public FirstOrderMarkovModel flattenHighOrderMarkovModel(HighOrderMarkovModel model) {
+        int markovOrder = model.trieOrder - 1;
+        FirstOrderMarkovModel firstOrderModel = new FirstOrderMarkovModel(new Random());
+        firstOrderModel.trieOrder = 2;
+        if (markovOrder == 1) {
+            firstOrderModel.trie = new Trie<Event>(model.trie);
+            firstOrderModel.trieOrder = 2;
+        }
+        else {
+            firstOrderTrie = new Trie<Event>();
+            TrieNode<Event> rootNode = model.trie.find(null);
+            generateFirstOrderTrie(rootNode, new LinkedList<String>(), markovOrder);
+            firstOrderTrie.updateKnownSymbols();
+            firstOrderModel.trie = firstOrderTrie;
+        }
+
+        return firstOrderModel;
+    }
+
+    /**
+     * <p>
+     * <b>This method is not available yet and always return null!</b>
+     * </p>
+     * <p>
+     * Takes a {@link PredictionByPartialMatch} model and returns a {@link FirstOrderMarkovModel}
+     * that conserves the high-order memory through state splitting.
+     * </p>
+     * 
+     * @param model
+     *            model that is flattened
+     * @return flattened first-order Markov model
+     */
+    public FirstOrderMarkovModel flattenPredictionByPartialMatch(PredictionByPartialMatch model) {
+        // TODO implement method
+        return null;
+    }
+
+    /**
+     * <p>
+     * Converts all nodes of the given depth into first-order node through state-splitting. For each
+     * node at the given depth a new node is created and appropriate transitions will be added.
+     * </p>
+     * <p>
+     * This method traverses through the tree recursively. If the recursion reaches the desired
+     * depth in the tree, node are added.
+     * </p>
+     * 
+     * @param currentNode
+     *            node whose sub-trie is currently traversed
+     * @param parentIDs
+     *            ID strings of the ancestors of the currentNode
+     * @param depth
+     *            depth to go - NOT the current depth.
+     */
+    private void generateFirstOrderTrie(TrieNode<Event> currentNode,
+                                        List<String> parentIDs,
+                                        int depth)
+    {
+        for (TrieNode<Event> child : currentNode.getChildren()) {
+            String currentId = child.getSymbol().getId();
+            if (depth > 1) {
+                List<String> childParentIDs = new LinkedList<String>(parentIDs);
+                childParentIDs.add(currentId);
+                generateFirstOrderTrie(child, childParentIDs, depth - 1);
+
+            }
+            else {
+                StringBuilder firstOrderID = new StringBuilder();
+                for (String parentID : parentIDs) {
+                    firstOrderID.append(parentID + EVENT_SEPARATOR);
+                }
+                firstOrderID.append(currentId);
+                TrieNode<Event> firstOrderNode =
+                    firstOrderTrie.getChildCreate(new Event(new StringEventType(firstOrderID
+                        .toString())));
+                firstOrderNode.setCount(child.getCount());
+                for (TrieNode<Event> transitionChild : child.getChildren()) {
+                    StringBuilder transitionID = new StringBuilder();
+                    for (String parentID : parentIDs.subList(1, parentIDs.size())) {
+                        transitionID.append(parentID + EVENT_SEPARATOR);
+                    }
+                    transitionID.append(currentId + EVENT_SEPARATOR);
+                    transitionID.append(transitionChild.getSymbol().getId());
+                    TrieNode<Event> firstOrderTransitionChild =
+                        firstOrderNode.getChildCreate(new Event(new StringEventType(transitionID
+                            .toString())));
+                    firstOrderTransitionChild.setCount(transitionChild.getCount());
+                }
+            }
+        }
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatch.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatch.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/PredictionByPartialMatch.java	(revision 922)
@@ -0,0 +1,198 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+
+/**
+ * <p>
+ * Implements Prediction by Partial Match (PPM) based on the following formula (LaTeX-style
+ * notation):<br>
+ * P_{PPM}(X_n|X_{n-1},...,X_{n-k}) = \sum_{i=k}^min escape^{k-i} P_{MM^i}(X_n
+ * |X_{n-1},...,X_{n-i})(1-escape)+escape^(k-min)P(X_n|X_{n-i},... ,X_{n-min})<br>
+ * P_{MM^i} denotes the probability in an i-th order Markov model.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * 
+ */
+public class PredictionByPartialMatch extends TrieBasedModel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Minimum order of the Markov model.
+     * </p>
+     */
+    protected int minOrder;
+
+    /**
+     * <p>
+     * Probability to use a lower-order Markov model
+     * </p>
+     */
+    protected double probEscape;
+
+    /**
+     * <p>
+     * Constructor. Creates a new PredictionByPartialMatch model with a given Markov order and a
+     * default escape probability of 0.1.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     */
+    public PredictionByPartialMatch(int markovOrder, Random r) {
+        this(markovOrder, r, 0.1);
+    }
+
+    /**
+     * <p>
+     * Creates a new PredictionByPartialMatch model with a given Markov order and escape
+     * probability.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     * @param probEscape
+     *            escape probability used by the model
+     */
+    public PredictionByPartialMatch(int markovOrder, Random r, double probEscape) {
+        this(markovOrder, 0, r, probEscape);
+    }
+
+    /**
+     * <p>
+     * Creates a new PredictionByPartialMatch model with a given Markov order and escape
+     * probability.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param minOrder
+     *            minimum order of the model; if this order is reached, there is no escape
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     * @param probEscape
+     *            escape probability used by the model
+     * @throws IllegalArgumentException
+     *             thrown if minOrder is less than 0 or greater than markovOrder or probEscape is
+     *             not in the interval (0,1)
+     */
+    public PredictionByPartialMatch(int markovOrder, int minOrder, Random r, double probEscape) {
+        super(markovOrder, r);
+        if (minOrder < 0) {
+            throw new IllegalArgumentException("minOrder must be greather than or equal to 0");
+        }
+        if (minOrder > markovOrder) {
+            throw new IllegalArgumentException(
+                                                "minOrder must be less than or equal to markovOrder");
+        }
+        if (probEscape <= 0.0 || probEscape >= 1.0) {
+            throw new IllegalArgumentException("probEscape must be in the interval (0,1)");
+        }
+        this.probEscape = probEscape;
+        this.minOrder = minOrder;
+    }
+
+    /**
+     * <p>
+     * Sets the escape probability of the model.
+     * </p>
+     * 
+     * @param probEscape
+     *            new escape probability
+     */
+    public void setProbEscape(double probEscape) {
+        this.probEscape = probEscape;
+    }
+
+    /**
+     * <p>
+     * Returns the escape probability of the model.
+     * </p>
+     * 
+     * @return escape probability of the model
+     */
+    public double getProbEscape() {
+        return probEscape;
+    }
+
+    /**
+     * <p>
+     * Calculates the probability of the next event based on the formula:<br>
+     * P_{PPM}(X_n|X_{n-1},...,X_{n-k}) = \sum_{i=k}^min escape^{k-i} P_{MM^i}(X_n
+     * |X_{n-1},...,X_{n-i})(1-escape)+escape^(k-min)P(X_n|X_{n-i},... ,X_{n-min})<br>
+     * P_{MM^i} denotes the probability in an i-th order Markov model.
+     * </p>
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util.List,
+     *      de.ugoe.cs.autoquest.eventcore.Event)
+     */
+    @Override
+    public double getProbability(List<Event> context, Event symbol) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (symbol == null) {
+            throw new IllegalArgumentException("symbol must not be null");
+        }
+        double result = 0.0d;
+        double resultCurrentContex = 0.0d;
+        double resultShorterContex = 0.0d;
+
+        List<Event> contextCopy;
+        if (context.size() >= trieOrder) {
+            contextCopy =
+                new LinkedList<Event>(context.subList(context.size() - trieOrder + 1,
+                                                      context.size()));
+        }
+        else {
+            contextCopy = new LinkedList<Event>(context);
+        }
+
+        Collection<Event> followers = trie.getFollowingSymbols(contextCopy); // \Sigma'
+        int sumCountFollowers = 0; // N(s\sigma')
+        for (Event follower : followers) {
+            sumCountFollowers += trie.getCount(contextCopy, follower);
+        }
+
+        int countSymbol = trie.getCount(contextCopy, symbol); // N(s\sigma)
+        if (sumCountFollowers == 0) {
+            resultCurrentContex = 0.0;
+        }
+        else {
+            resultCurrentContex = (double) countSymbol / sumCountFollowers;
+        }
+        if (contextCopy.size() > minOrder) {
+            resultCurrentContex *= (1 - probEscape);
+            contextCopy.remove(0);
+            if (contextCopy.size() >= minOrder) {
+                double probSuffix = getProbability(contextCopy, symbol);
+
+                if (followers.size() == 0) {
+                    resultShorterContex = probSuffix;
+                }
+                else {
+                    resultShorterContex = probEscape * probSuffix;
+                }
+            }
+        }
+        result = resultCurrentContex + resultShorterContex;
+
+        return result;
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/Trie.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/Trie.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/Trie.java	(revision 922)
@@ -0,0 +1,466 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.util.StringTools;
+
+import edu.uci.ics.jung.graph.DelegateTree;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.Tree;
+
+/**
+ * <p>
+ * This class implements a <it>trie</it>, i.e., a tree of sequences that the occurence of
+ * subsequences up to a predefined length. This length is the trie order.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * 
+ * @param <T>
+ *            Type of the symbols that are stored in the trie.
+ * 
+ * @see TrieNode
+ */
+public class Trie<T> implements IDotCompatible, Serializable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Collection of all symbols occuring in the trie.
+     * </p>
+     */
+    private Collection<T> knownSymbols;
+
+    /**
+     * <p>
+     * Reference to the root of the trie.
+     * </p>
+     */
+    private final TrieNode<T> rootNode;
+
+    /**
+     * <p>
+     * Contructor. Creates a new Trie.
+     * </p>
+     */
+    public Trie() {
+        rootNode = new TrieNode<T>();
+        knownSymbols = new LinkedHashSet<T>();
+    }
+
+    /**
+     * <p>
+     * Copy-Constructor. Creates a new Trie as the copy of other. The other trie must noch be null.
+     * </p>
+     * 
+     * @param other
+     *            Trie that is copied
+     */
+    public Trie(Trie<T> other) {
+        if (other == null) {
+            throw new IllegalArgumentException("other trie must not be null");
+        }
+        rootNode = new TrieNode<T>(other.rootNode);
+        knownSymbols = new LinkedHashSet<T>(other.knownSymbols);
+    }
+
+    /**
+     * <p>
+     * Returns a collection of all symbols occuring in the trie.
+     * </p>
+     * 
+     * @return symbols occuring in the trie
+     */
+    public Collection<T> getKnownSymbols() {
+        return new LinkedHashSet<T>(knownSymbols);
+    }
+
+    /**
+     * <p>
+     * Trains the current trie using the given sequence and adds all subsequence of length
+     * {@code maxOrder}.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence whose subsequences are added to the trie
+     * @param maxOrder
+     *            maximum length of the subsequences added to the trie
+     */
+    public void train(List<T> sequence, int maxOrder) {
+        if (maxOrder < 1) {
+            return;
+        }
+        IncompleteMemory<T> latestActions = new IncompleteMemory<T>(maxOrder);
+        int i = 0;
+        for (T currentEvent : sequence) {
+            latestActions.add(currentEvent);
+            knownSymbols.add(currentEvent);
+            i++;
+            if (i >= maxOrder) {
+                add(latestActions.getLast(maxOrder));
+            }
+        }
+        int sequenceLength = sequence.size();
+        for (int j = maxOrder - 1; j > 0; j--) {
+            add(sequence.subList(sequenceLength - j, sequenceLength));
+        }
+    }
+
+    /**
+     * <p>
+     * Adds a given subsequence to the trie and increases the counters accordingly.
+     * </p>
+     * 
+     * @param subsequence
+     *            subsequence whose counters are increased
+     * @see TrieNode#add(List)
+     */
+    protected void add(List<T> subsequence) {
+        if (subsequence != null && !subsequence.isEmpty()) {
+            knownSymbols.addAll(subsequence);
+            subsequence = new LinkedList<T>(subsequence); // defensive copy!
+            T firstSymbol = subsequence.get(0);
+            TrieNode<T> node = getChildCreate(firstSymbol);
+            node.add(subsequence);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the child of the root node associated with the given symbol or creates it if it does
+     * not exist yet.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol
+     * @see TrieNode#getChildCreate(Object)
+     */
+    protected TrieNode<T> getChildCreate(T symbol) {
+        return rootNode.getChildCreate(symbol);
+    }
+
+    /**
+     * <p>
+     * Returns the child of the root node associated with the given symbol or null if it does not
+     * exist.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol; null if no such node exists
+     * @see TrieNode#getChild(Object)
+     */
+    protected TrieNode<T> getChild(T symbol) {
+        return rootNode.getChild(symbol);
+    }
+
+    /**
+     * <p>
+     * Returns the number of occurences of the given sequence.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence whose number of occurences is required
+     * @return number of occurences of the sequence
+     */
+    public int getCount(List<T> sequence) {
+        int count = 0;
+        TrieNode<T> node = find(sequence);
+        if (node != null) {
+            count = node.getCount();
+        }
+        return count;
+    }
+
+    /**
+     * <p>
+     * Returns the number of occurences of the given prefix and a symbol that follows it.<br>
+     * Convenience function to simplify usage of {@link #getCount(List)}.
+     * </p>
+     * 
+     * @param sequence
+     *            prefix of the sequence
+     * @param follower
+     *            suffix of the sequence
+     * @return number of occurences of the sequence
+     * @see #getCount(List)
+     */
+    public int getCount(List<T> sequence, T follower) {
+        List<T> tmpSequence = new LinkedList<T>(sequence);
+        tmpSequence.add(follower);
+        return getCount(tmpSequence);
+
+    }
+
+    /**
+     * <p>
+     * Searches the trie for a given sequence and returns the node associated with the sequence or
+     * null if no such node is found.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence that is searched for
+     * @return node associated with the sequence
+     * @see TrieNode#find(List)
+     */
+    public TrieNode<T> find(List<T> sequence) {
+        if (sequence == null || sequence.isEmpty()) {
+            return rootNode;
+        }
+        List<T> sequenceCopy = new LinkedList<T>(sequence);
+        TrieNode<T> result = null;
+        TrieNode<T> node = getChild(sequenceCopy.get(0));
+        if (node != null) {
+            sequenceCopy.remove(0);
+            result = node.find(sequenceCopy);
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Returns a collection of all symbols that follow a given sequence in the trie. In case the
+     * sequence is not found or no symbols follow the sequence the result will be empty.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence whose followers are returned
+     * @return symbols following the given sequence
+     * @see TrieNode#getFollowingSymbols()
+     */
+    public Collection<T> getFollowingSymbols(List<T> sequence) {
+        Collection<T> result = new LinkedList<T>();
+        TrieNode<T> node = find(sequence);
+        if (node != null) {
+            result = node.getFollowingSymbols();
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Returns the longest suffix of the given context that is contained in the tree and whose
+     * children are leaves.
+     * </p>
+     * 
+     * @param context
+     *            context whose suffix is searched for
+     * @return longest suffix of the context
+     */
+    public List<T> getContextSuffix(List<T> context) {
+        List<T> contextSuffix;
+        if (context != null) {
+            contextSuffix = new LinkedList<T>(context); // defensive copy
+        }
+        else {
+            contextSuffix = new LinkedList<T>();
+        }
+        boolean suffixFound = false;
+
+        while (!suffixFound) {
+            if (contextSuffix.isEmpty()) {
+                suffixFound = true; // suffix is the empty word
+            }
+            else {
+                TrieNode<T> node = find(contextSuffix);
+                if (node != null) {
+                    if (!node.getFollowingSymbols().isEmpty()) {
+                        suffixFound = true;
+                    }
+                }
+                if (!suffixFound) {
+                    contextSuffix.remove(0);
+                }
+            }
+        }
+
+        return contextSuffix;
+    }
+
+    /**
+     * <p>
+     * Helper class for graph visualization of a trie.
+     * </p>
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    static public class Edge {}
+
+    /**
+     * <p>
+     * Helper class for graph visualization of a trie.
+     * </p>
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    static public class TrieVertex {
+
+        /**
+         * <p>
+         * Id of the vertex.
+         * </p>
+         */
+        private String id;
+
+        /**
+         * <p>
+         * Contructor. Creates a new TrieVertex.
+         * </p>
+         * 
+         * @param id
+         *            id of the vertex
+         */
+        protected TrieVertex(String id) {
+            this.id = id;
+        }
+
+        /**
+         * <p>
+         * Returns the id of the vertex.
+         * </p>
+         * 
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return id;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a {@link Graph} representation of the trie.
+     * </p>
+     * 
+     * @return {@link Graph} representation of the trie
+     */
+    protected Tree<TrieVertex, Edge> getGraph() {
+        DelegateTree<TrieVertex, Edge> graph = new DelegateTree<TrieVertex, Edge>();
+        rootNode.getGraph(null, graph);
+        return graph;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IDotCompatible#getDotRepresentation()
+     */
+    public String getDotRepresentation() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("digraph model {" + StringTools.ENDLINE);
+        rootNode.appendDotRepresentation(stringBuilder);
+        stringBuilder.append('}' + StringTools.ENDLINE);
+        return stringBuilder.toString();
+    }
+
+    /**
+     * <p>
+     * Returns the string representation of the root node.
+     * </p>
+     * 
+     * @see TrieNode#toString()
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return rootNode.toString();
+    }
+
+    /**
+     * <p>
+     * Returns the number of symbols contained in the trie.
+     * </p>
+     * 
+     * @return number of symbols contained in the trie
+     */
+    public int getNumSymbols() {
+        return knownSymbols.size();
+    }
+
+    /**
+     * <p>
+     * Returns the number of trie nodes that are ancestors of a leaf. This is the equivalent to the
+     * number of states a first-order markov model would have.
+     * <p>
+     * 
+     * @return number of trie nodes that are ancestors of leafs.
+     */
+    public int getNumLeafAncestors() {
+        List<TrieNode<T>> ancestors = new LinkedList<TrieNode<T>>();
+        rootNode.getLeafAncestors(ancestors);
+        return ancestors.size();
+    }
+
+    /**
+     * <p>
+     * Returns the number of trie nodes that are leafs.
+     * </p>
+     * 
+     * @return number of leafs in the trie
+     */
+    public int getNumLeafs() {
+        return rootNode.getNumLeafs();
+    }
+
+    /**
+     * <p>
+     * Updates the list of known symbols by replacing it with all symbols that are found in the
+     * child nodes of the root node. This should be the same as all symbols that are contained in
+     * the trie.
+     * </p>
+     */
+    public void updateKnownSymbols() {
+        knownSymbols = new HashSet<T>();
+        for (TrieNode<T> node : rootNode.getChildren()) {
+            knownSymbols.add(node.getSymbol());
+        }
+    }
+
+    /**
+     * <p>
+     * Two Tries are defined as equal, if their {@link #rootNode} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @SuppressWarnings("rawtypes")
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other instanceof Trie) {
+            return rootNode.equals(((Trie) other).rootNode);
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+        if (rootNode != null) {
+            hash = multiplier * hash + rootNode.hashCode();
+        }
+        return hash;
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModel.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModel.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieBasedModel.java	(revision 922)
@@ -0,0 +1,402 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.Edge;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.TrieVertex;
+import edu.uci.ics.jung.graph.Tree;
+
+/**
+ * <p>
+ * Implements a skeleton for stochastic processes that can calculate probabilities based on a trie.
+ * The skeleton provides all functionalities of {@link IStochasticProcess} except
+ * {@link IStochasticProcess#getProbability(List, Event)}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public abstract class TrieBasedModel implements IStochasticProcess {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * The order of the trie, i.e., the maximum length of subsequences stored in the trie.
+     * </p>
+     */
+    protected int trieOrder;
+
+    /**
+     * <p>
+     * Trie on which the probability calculations are based.
+     * </p>
+     */
+    protected Trie<Event> trie = null;
+
+    /**
+     * <p>
+     * Random number generator used by probabilistic sequence generation methods.
+     * </p>
+     */
+    protected final Random r;
+
+    /**
+     * <p>
+     * Constructor. Creates a new TrieBasedModel that can be used for stochastic processes with a
+     * Markov order less than or equal to {@code markovOrder}.
+     * </p>
+     * 
+     * @param markovOrder
+     *            Markov order of the model
+     * @param r
+     *            random number generator used by probabilistic methods of the class
+     * @throws IllegalArgumentException
+     *             thrown if markovOrder is less than 0 or the random number generator r is null
+     */
+    public TrieBasedModel(int markovOrder, Random r) {
+        super();
+        if (markovOrder < 0) {
+            throw new IllegalArgumentException("markov order must not be less than 0");
+        }
+        if (r == null) {
+            throw new IllegalArgumentException("random number generator r must not be null");
+        }
+        this.trieOrder = markovOrder + 1;
+        this.r = r;
+    }
+
+    /**
+     * <p>
+     * Trains the model by generating a trie from which probabilities are calculated. The trie is
+     * newly generated based solely on the passed sequences. If an existing model should only be
+     * updated, use {@link #update(Collection)} instead.
+     * </p>
+     * 
+     * @param sequences
+     *            training data
+     * @throws IllegalArgumentException
+     *             thrown is sequences is null
+     */
+    public void train(Collection<List<Event>> sequences) {
+        trie = null;
+        update(sequences);
+    }
+
+    /**
+     * <p>
+     * Trains the model by updating the trie from which the probabilities are calculated. This
+     * function updates an existing trie. In case no trie exists yet, a new trie is generated and
+     * the function behaves like {@link #train(Collection)}.
+     * </p>
+     * 
+     * @param sequences
+     *            training data
+     * @throws IllegalArgumentException
+     *             thrown is sequences is null
+     */
+    public void update(Collection<List<Event>> sequences) {
+        if (sequences == null) {
+            throw new IllegalArgumentException("sequences must not be null");
+        }
+        if (trie == null) {
+            trie = new Trie<Event>();
+        }
+        for (List<Event> sequence : sequences) {
+            List<Event> currentSequence = new LinkedList<Event>(sequence); // defensive
+                                                                           // copy
+            currentSequence.add(0, Event.STARTEVENT);
+            currentSequence.add(Event.ENDEVENT);
+
+            trie.train(currentSequence, trieOrder);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#randomSequence()
+     */
+    @Override
+    public List<Event> randomSequence() {
+        return randomSequence(Integer.MAX_VALUE, true);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#randomSequence()
+     */
+    @Override
+    public List<Event> randomSequence(int maxLength, boolean validEnd) {
+        List<Event> sequence = new LinkedList<Event>();
+        if (trie != null) {
+            boolean endFound = false;
+            while (!endFound) { // outer loop for length checking
+                sequence = new LinkedList<Event>();
+                IncompleteMemory<Event> context = new IncompleteMemory<Event>(trieOrder - 1);
+                context.add(Event.STARTEVENT);
+
+                while (!endFound && sequence.size() <= maxLength) {
+                    double randVal = r.nextDouble();
+                    double probSum = 0.0;
+                    List<Event> currentContext = context.getLast(trieOrder);
+                    for (Event symbol : trie.getKnownSymbols()) {
+                        probSum += getProbability(currentContext, symbol);
+                        if (probSum >= randVal) {
+                            if (!(Event.STARTEVENT.equals(symbol) || Event.ENDEVENT.equals(symbol)))
+                            {
+                                // only add the symbol the sequence if it is not
+                                // START or END
+                                context.add(symbol);
+                                sequence.add(symbol);
+                            }
+                            endFound =
+                                (Event.ENDEVENT.equals(symbol)) ||
+                                    (!validEnd && sequence.size() == maxLength);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return sequence;
+    }
+
+    /**
+     * <p>
+     * Returns a Dot representation of the internal trie.
+     * </p>
+     * 
+     * @return dot representation of the internal trie
+     */
+    public String getTrieDotRepresentation() {
+        if (trie == null) {
+            return "";
+        }
+        else {
+            return trie.getDotRepresentation();
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a {@link Tree} of the internal trie that can be used for visualization.
+     * </p>
+     * 
+     * @return {@link Tree} depicting the internal trie
+     */
+    public Tree<TrieVertex, Edge> getTrieGraph() {
+        if (trie == null) {
+            return null;
+        }
+        else {
+            return trie.getGraph();
+        }
+    }
+
+    /**
+     * <p>
+     * The string representation of the model is {@link Trie#toString()} of {@link #trie}.
+     * </p>
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (trie == null) {
+            return "";
+        }
+        else {
+            return trie.toString();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getNumStates()
+     */
+    @Override
+    public int getNumSymbols() {
+        if (trie == null) {
+            return 0;
+        }
+        else {
+            return trie.getNumSymbols();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getStateStrings()
+     */
+    @Override
+    public String[] getSymbolStrings() {
+        if (trie == null) {
+            return new String[0];
+        }
+        String[] stateStrings = new String[getNumSymbols()];
+        int i = 0;
+        for (Event symbol : trie.getKnownSymbols()) {
+            if (symbol.toString() == null) {
+                stateStrings[i] = "null";
+            }
+            else {
+                stateStrings[i] = symbol.toString();
+            }
+            i++;
+        }
+        return stateStrings;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getEvents()
+     */
+    @Override
+    public Collection<Event> getEvents() {
+        if (trie == null) {
+            return new HashSet<Event>();
+        }
+        else {
+            return trie.getKnownSymbols();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#generateSequences(int)
+     */
+    @Override
+    public Collection<List<Event>> generateSequences(int length) {
+        return generateSequences(length, false);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#generateSequences(int, boolean)
+     */
+    @Override
+    public Set<List<Event>> generateSequences(int length, boolean fromStart) {
+        Set<List<Event>> sequenceSet = new LinkedHashSet<List<Event>>();
+        if (length < 1) {
+            throw new IllegalArgumentException(
+                                                "Length of generated subsequences must be at least 1.");
+        }
+        if (length == 1) {
+            if (fromStart) {
+                List<Event> subSeq = new LinkedList<Event>();
+                subSeq.add(Event.STARTEVENT);
+                sequenceSet.add(subSeq);
+            }
+            else {
+                for (Event event : getEvents()) {
+                    List<Event> subSeq = new LinkedList<Event>();
+                    subSeq.add(event);
+                    sequenceSet.add(subSeq);
+                }
+            }
+            return sequenceSet;
+        }
+        Collection<Event> events = getEvents();
+        Collection<List<Event>> seqsShorter = generateSequences(length - 1, fromStart);
+        for (Event event : events) {
+            for (List<Event> seqShorter : seqsShorter) {
+                Event lastEvent = event;
+                if (getProbability(seqShorter, lastEvent) > 0.0) {
+                    List<Event> subSeq = new ArrayList<Event>(seqShorter);
+                    subSeq.add(lastEvent);
+                    sequenceSet.add(subSeq);
+                }
+            }
+        }
+        return sequenceSet;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#generateValidSequences (int)
+     */
+    @Override
+    public Collection<List<Event>> generateValidSequences(int length) {
+        // check for min-length implicitly done by generateSequences
+        Collection<List<Event>> allSequences = generateSequences(length, true);
+        Collection<List<Event>> validSequences = new LinkedHashSet<List<Event>>();
+        for (List<Event> sequence : allSequences) {
+            if (sequence.size() == length &&
+                Event.ENDEVENT.equals(sequence.get(sequence.size() - 1)))
+            {
+                validSequences.add(sequence);
+            }
+        }
+        return validSequences;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getProbability(java.util .List)
+     */
+    @Override
+    public double getProbability(List<Event> sequence) {
+        if (sequence == null) {
+            throw new IllegalArgumentException("sequence must not be null");
+        }
+        double prob = 1.0;
+        List<Event> context = new LinkedList<Event>();
+        for (Event event : sequence) {
+            prob *= getProbability(context, event);
+            context.add(event);
+        }
+        return prob;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getNumFOMStates()
+     */
+    @Override
+    public int getNumFOMStates() {
+        if (trie == null) {
+            return 0;
+        }
+        else {
+            return trie.getNumLeafAncestors();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess#getNumTransitions()
+     */
+    @Override
+    public int getNumTransitions() {
+        if (trie == null) {
+            return 0;
+        }
+        else {
+            return trie.getNumLeafs();
+        }
+    }
+}
Index: trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieNode.java
===================================================================
--- trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieNode.java	(revision 922)
+++ trunk/autoquest-core-usageprofiles/src/main/java/de/ugoe/cs/autoquest/usageprofiles/TrieNode.java	(revision 922)
@@ -0,0 +1,438 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.usageprofiles.Trie.Edge;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.TrieVertex;
+import de.ugoe.cs.util.StringTools;
+import edu.uci.ics.jung.graph.DelegateTree;
+import edu.uci.ics.jung.graph.Tree;
+
+/**
+ * <p>
+ * This class implements a node of a trie. Each node is associated with a symbol and has a counter.
+ * The counter marks the number of occurences of the sequence defined by the path from the root of
+ * the trie to this node.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * 
+ * @param <T>
+ *            Type of the symbols that are stored in the trie.
+ * @see Trie
+ */
+class TrieNode<T> implements Serializable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Counter for the number of occurences of the sequence.
+     * </p>
+     */
+    private int count;
+
+    /**
+     * <p>
+     * Symbol associated with the node.
+     * </p>
+     */
+    private final T symbol;
+
+    /**
+     * <p>
+     * Child nodes of this node. If the node is a leaf this collection is empty.
+     * </p>
+     */
+    private Collection<TrieNode<T>> children;
+
+    /**
+     * <p>
+     * Constructor. Creates a new TrieNode without a symbol associated.<br>
+     * <b>This constructor should only be used to create the root node of the trie!</b>
+     * </p>
+     */
+    TrieNode() {
+        this.symbol = null;
+        count = 0;
+        children = new LinkedList<TrieNode<T>>();
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new TrieNode. The symbol must not be null.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol associated with the trie node
+     */
+    TrieNode(T symbol) {
+        if (symbol == null) {
+            throw new IllegalArgumentException(
+                                                "symbol must not be null. null is reserved for root node!");
+        }
+        this.symbol = symbol;
+        count = 0;
+        children = new LinkedList<TrieNode<T>>();
+    }
+
+    /**
+     * <p>
+     * Copy-Constructor. Creates a new TrieNode as copy of other. Other must not be null.
+     * </p>
+     * 
+     * @param other
+     */
+    TrieNode(TrieNode<T> other) {
+        if (other == null) {
+            throw new IllegalArgumentException("other must not be null");
+        }
+        symbol = other.symbol;
+        count = other.count;
+        children = new LinkedList<TrieNode<T>>();
+        for (TrieNode<T> child : other.children) {
+            children.add(new TrieNode<T>(child));
+        }
+    }
+
+    /**
+     * <p>
+     * Adds a given subsequence to the trie and increases the counters accordingly.
+     * </p>
+     * 
+     * @param subsequence
+     *            subsequence whose counters are increased
+     * @see Trie#add(List)
+     */
+    public void add(List<T> subsequence) {
+        if (!subsequence.isEmpty()) {
+            if (!symbol.equals(subsequence.get(0))) { // should be guaranteed by
+                                                      // the
+                                                      // recursion/TrieRoot!
+                throw new AssertionError("Invalid trie operation!");
+            }
+            count++;
+            subsequence.remove(0);
+            if (!subsequence.isEmpty()) {
+                T nextSymbol = subsequence.get(0);
+                getChildCreate(nextSymbol).add(subsequence);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the symbol associated with the node.
+     * </p>
+     * 
+     * @return symbol associated with the node
+     */
+    public T getSymbol() {
+        return symbol;
+    }
+
+    /**
+     * <p>
+     * Returns the number of occurences of the sequence represented by the node.
+     * </p>
+     * 
+     * @return number of occurences of the sequence represented by the node
+     */
+    public int getCount() {
+        return count;
+    }
+
+    /**
+     * <p>
+     * Returns the child of the node associated with the given symbol or creates it if it does not
+     * exist yet.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol
+     * @see Trie#getChildCreate(Object)
+     */
+    protected TrieNode<T> getChildCreate(T symbol) {
+        TrieNode<T> node = getChild(symbol);
+        if (node == null) {
+            node = new TrieNode<T>(symbol);
+            children.add(node);
+        }
+        return node;
+    }
+
+    /**
+     * <p>
+     * Returns the child of the node associated with the given symbol or null if it does not exist.
+     * </p>
+     * 
+     * @param symbol
+     *            symbol whose node is required
+     * @return node associated with the symbol; null if no such node exists
+     * @see Trie#getChild(Object)
+     */
+    protected TrieNode<T> getChild(T symbol) {
+        for (TrieNode<T> child : children) {
+            if (child.getSymbol().equals(symbol)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * <p>
+     * Returns all children of this node.
+     * </p>
+     * 
+     * @return children of this node
+     */
+    protected Collection<TrieNode<T>> getChildren() {
+        return children;
+    }
+
+    /**
+     * <p>
+     * Searches the sub-trie of this trie node for a given sequence and returns the node associated
+     * with the sequence or null if no such node is found.
+     * </p>
+     * 
+     * @param sequence
+     *            sequence that is searched for
+     * @return node associated with the sequence
+     * @see Trie#find(List)
+     */
+    public TrieNode<T> find(List<T> subsequence) {
+        TrieNode<T> result = null;
+        if (subsequence.isEmpty()) {
+            result = this;
+        }
+        else {
+            TrieNode<T> node = getChild(subsequence.get(0));
+            if (node != null) {
+                subsequence.remove(0);
+                result = node.find(subsequence);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * <p>
+     * Returns a collection of all symbols that follow a this node, i.e., the symbols associated
+     * with the children of this node.
+     * </p>
+     * 
+     * @return symbols follow this node
+     * @see TrieNode#getFollowingSymbols()
+     */
+    public Collection<T> getFollowingSymbols() {
+        Collection<T> followingSymbols = new LinkedList<T>();
+        for (TrieNode<T> child : children) {
+            followingSymbols.add(child.getSymbol());
+        }
+        return followingSymbols;
+    }
+
+    /**
+     * <p>
+     * The string representation of a node is {@code symbol.toString()#count}
+     * </p>
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        String str = symbol.toString() + " #" + count;
+        if (!children.isEmpty()) {
+            str += StringTools.ENDLINE + children.toString();
+        }
+        return str;
+    }
+
+    /**
+     * <p>
+     * Generates a {@link Tree} represenation of the trie.
+     * </p>
+     * 
+     * @param parent
+     *            parent vertex in the generated tree
+     * @param graph
+     *            complete tree
+     */
+    void getGraph(TrieVertex parent, DelegateTree<TrieVertex, Edge> graph) {
+        TrieVertex currentVertex;
+        if (isRoot()) {
+            currentVertex = new TrieVertex("root");
+            graph.addVertex(currentVertex);
+        }
+        else {
+            currentVertex = new TrieVertex(getSymbol().toString() + "#" + getCount());
+            graph.addChild(new Edge(), parent, currentVertex);
+        }
+        for (TrieNode<T> node : children) {
+            node.getGraph(currentVertex, graph);
+        }
+    }
+
+    /**
+     * <p>
+     * Appends the current node to the dot representation of the trie.
+     * </p>
+     * 
+     * @param stringBuilder
+     *            {@link StringBuilder} to which the dot representation is appended
+     */
+    void appendDotRepresentation(StringBuilder stringBuilder) {
+        String thisSaneId;
+        if (isRoot()) {
+            thisSaneId = "root";
+        }
+        else {
+            thisSaneId =
+                symbol.toString().replace("\"", "\\\"").replaceAll("[\r\n]", "") + "#" + count;
+        }
+        stringBuilder.append(" " + hashCode() + " [label=\"" + thisSaneId + "\"];" +
+            StringTools.ENDLINE);
+        for (TrieNode<T> childNode : children) {
+            stringBuilder.append(" " + hashCode() + " -> " + childNode.hashCode() + ";" +
+                StringTools.ENDLINE);
+        }
+        for (TrieNode<T> childNode : children) {
+            childNode.appendDotRepresentation(stringBuilder);
+        }
+    }
+
+    /**
+     * <p>
+     * Checks if the node is a leaf.
+     * </p>
+     * 
+     * @return true if the node is a leaf, false otherwise.
+     */
+    protected boolean isLeaf() {
+        return children.isEmpty();
+    }
+
+    /**
+     * <p>
+     * Checks if the node is the root.
+     * </p>
+     * 
+     * @return true if the node is the root of the trie, false otherwise
+     */
+    protected boolean isRoot() {
+        return symbol == null;
+    }
+
+    /**
+     * <p>
+     * Recursive methods that collects all nodes that are ancestors of leafs and stores them in the
+     * set.
+     * </p>
+     * 
+     * @param ancestors
+     *            set of all ancestors of leafs
+     */
+    protected void getLeafAncestors(List<TrieNode<T>> ancestors) {
+        boolean isAncestor = false;
+        for (TrieNode<T> child : children) {
+            child.getLeafAncestors(ancestors);
+            isAncestor |= child.isLeaf();
+        }
+        if (isAncestor) {
+            ancestors.add(this);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the number of descendants of this node that are leafs. This does not only include
+     * direct children of this node, but all leafs in the sub-trie with this node as root.
+     * </p>
+     * 
+     * @return number of leafs in this sub-trie
+     */
+    protected int getNumLeafs() {
+        int numLeafs = 0;
+        for (TrieNode<T> child : children) {
+            if (child.isLeaf()) {
+                numLeafs++;
+            }
+            else {
+                numLeafs += child.getNumLeafs();
+            }
+        }
+        return numLeafs;
+    }
+
+    /**
+     * <p>
+     * Sets the {@link #count} of this node.
+     * </p>
+     * <p>
+     * This function should only be used sparingly and very carefully! The count is usually
+     * maintained automatically by the training procedures.
+     * </p>
+     * 
+     * @param count
+     *            new count
+     */
+    protected void setCount(int count) {
+        this.count = count;
+    }
+
+    /**
+     * <p>
+     * Two TrieNodes are defined as equal, if their {@link #count}, {@link #symbol}, and
+     * {@link #children} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @SuppressWarnings("rawtypes")
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other instanceof TrieNode) {
+            TrieNode otherNode = (TrieNode) other;
+            if (symbol == null) {
+                return count == otherNode.count && otherNode.symbol == null &&
+                    children.equals(otherNode.children);
+            }
+            else {
+                return count == otherNode.count && symbol.equals(((TrieNode) other).symbol) &&
+                    children.equals(((TrieNode) other).children);
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+        if (symbol != null) {
+            hash = multiplier * hash + symbol.hashCode();
+        }
+        hash = multiplier * hash + count;
+        hash = multiplier * hash + children.hashCode();
+        return hash;
+    }
+}
Index: trunk/autoquest-htmlmonitor-test/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-htmlmonitor-test/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-htmlmonitor-test/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-htmlmonitor-test/src/test/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServerTest.java
===================================================================
--- trunk/autoquest-htmlmonitor-test/src/test/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServerTest.java	(revision 921)
+++ trunk/autoquest-htmlmonitor-test/src/test/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServerTest.java	(revision 922)
@@ -371,5 +371,5 @@
     
     /* (non-Javadoc)
-     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitoringListener#handleEvents(de.ugoe.cs.quest.htmlmonitor.HtmlClientInfos, de.ugoe.cs.quest.htmlmonitor.HtmlEvent[])
+     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitoringListener#handleEvents(de.ugoe.cs.autoquest.htmlmonitor.HtmlClientInfos, de.ugoe.cs.autoquest.htmlmonitor.HtmlEvent[])
      */
     @Override
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCComponent.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCComponent.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCComponent.java	(revision 922)
@@ -0,0 +1,444 @@
+
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.event.ContainerListener;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.accessibility.AccessibleContext;
+
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * This class manages information about the current GUI. It always contains the current GUI
+ * hierarchy.
+ * </p>
+ * 
+ * @author Steffen Herbold 
+ * @author Fabian Glaser
+ * @version 1.0
+ */
+public class JFCComponent {
+
+    /**
+     * <p>
+     * Map of all known GUI components.
+     * </p>
+     */
+    private static Map<Component, JFCComponent> knownComponents =
+        new HashMap<Component, JFCComponent>();
+    
+    /**
+     * <p>
+     * List of PropertyChangeListeners that are registered on the components.
+     * </p>
+     */
+    private static List<PropertyChangeListener> propertyChangeListeners = 
+    	new ArrayList<PropertyChangeListener>();
+    
+    /**
+     * <p>
+     * List of ContainerListeners that are registered on the components.
+     * </p>
+     */
+    private static List<ContainerListener> containerListeners =
+    		new ArrayList<ContainerListener>();
+
+    /**
+     * <p>
+     * Adds a AWT component to the GUI hierarchy. If the component already exists in the hierarchy,
+     * it is not added a second time.
+     * </p>
+     * 
+     * @param component
+     *            component that is added
+     */
+    public static void add(Component component) {
+        add(component, find(component.getParent()));
+    }
+    
+    /**
+     * <p>
+     * Adds a new PropertyChangeListener to the components.
+     * </p> 
+     * @param list 
+     * 			the PropertyChangeListener
+     */
+    public static void addPropertyChangeListener(PropertyChangeListener list){
+    	propertyChangeListeners.add(list);
+    }
+    
+    /**
+     * <p> 
+     * Adds a new ContainerListener to the components.
+     * </p> 
+     * @param list
+     * 			the ContainerListener
+     */
+    public static void addContainerListener(ContainerListener list){
+    	containerListeners.add(list);
+    }
+    
+    /**
+     * <p>
+     * Tests if a component is already known.
+     * </p>
+     * @param component to be tested
+     * @return true, if component is already known, false otherwise.
+     */
+    public static boolean isKnown(Component component){
+    	if (knownComponents.containsKey(component))
+    		return true;
+    	return false;
+    }
+
+    /**
+     * <p>
+     * Adds a AWT component to the GUI hierarchy. If the component already exists in the hierarchy,
+     * it is not added a second time.
+     * </p>
+     * 
+     * @param component
+     *            component that is added
+     * @param parent
+     *            parent of the component
+     */
+    public static void add(Component component, JFCComponent parent) {
+        if (!knownComponents.containsKey(component)) {
+            knownComponents.put(component, new JFCComponent(component, parent));
+        }
+    }
+
+    /**
+     * <p>
+     * Finds a component in the GUI hierarchy and returns the corresponding JFComponent instance.
+     * Returns null if the component is not found.
+     * </p>
+     * 
+     * @param component
+     *            component that is searched for
+     * @return corresponding JFComponent instance; null if the compenent is not found
+     */
+    public static JFCComponent find(Component component) {
+        return knownComponents.get(component);
+    }
+
+    /**
+     * <p>
+     * Removes a component from the GUI hierarchy. In case the component is not part of the known
+     * hierachy, nothing happens.
+     * </p>
+     * 
+     * @param component
+     *            component to be removed
+     */
+    public static void remove(Component component) {
+        JFCComponent jfcComponent = knownComponents.remove(component);
+        if (jfcComponent != null) {
+            jfcComponent.removeFromParent();
+            jfcComponent.removeChildren();
+        }
+    }
+
+    /**
+     * <p>
+     * Parent of the GUI component. null means, that the component has no parent.
+     * </p>
+     */
+    private JFCComponent parent = null;
+
+    /**
+     * <p>
+     * Child components of the component.
+     * </p>
+     */
+    private List<JFCComponent> children = new LinkedList<JFCComponent>();
+
+    /**
+     * <p>
+     * Reference to the actual GUI component.
+     * </p>
+     */
+    private Component component;
+
+    /**
+     * <p>
+     * Helper attribute that contains the title of the component. Set by {@link #setTitle()}.
+     * </p>
+     */
+    private String title = null;
+
+    /**
+     * <p>
+     * Helper attribute that contains the class of the component. Set by {@link #setClass()}.
+     * </p>
+     */
+    private String componentClass = null;
+
+    /**
+     * <p>
+     * Helper attribute that contains the icon of the component. Set by {@link #setIcon()}.
+     * </p>
+     */
+    private String icon = null;
+
+    /**
+     * <p>
+     * Helper attribute that contains the icon of the component. Set by {@link #setIndex()}.
+     * </p>
+     */
+    private int index = -1;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCComponent. Only used internally by
+     * {@link #add(Component, JFCComponent)}.
+     * </p>
+     * 
+     * @param component
+     *            component associated with the JFCComponent
+     * @param parent
+     *            parent of the component; null if there is no parent
+     */
+    private JFCComponent(Component component, JFCComponent parent) {
+        if (component == null) {
+            throw new InvalidParameterException("parameter component must not be null");
+        }
+        this.component = component;
+        
+        // add PropertyChangeListeners to AccessibleContext
+        AccessibleContext context = component.getAccessibleContext();
+        if (context != null){
+        	for (PropertyChangeListener listener: propertyChangeListeners)
+        		context.addPropertyChangeListener(listener);
+        }
+        
+        // add PropertyChangeListeners to component itself 
+        for (PropertyChangeListener listener: propertyChangeListeners)
+        	this.component.addPropertyChangeListener(listener);
+        
+        this.parent = parent;
+        if (parent != null) {
+            parent.addChild(this);
+        }
+
+        if (component instanceof Container) {
+        	for (ContainerListener listener: containerListeners)
+        		((Container) component).addContainerListener(listener);
+        	
+            for (Component childComponent : ((Container) component).getComponents()) {
+                add(childComponent, this);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Adds a child component to the current component.
+     * </p>
+     * 
+     * @param child
+     *            child component to be added
+     */
+    private void addChild(JFCComponent child) {
+        children.add(child);
+    }
+
+    /**
+     * <p>
+     * Returns an XML representation of the component.
+     * </p>
+     * 
+     * @return XLM snippet
+     */
+    public String getXML() {
+        setClass();
+        setIcon();
+        setIndex();
+        setTitle();
+        StringBuilder builder = new StringBuilder();
+        builder.append("<component");
+        if (parent != null){
+        	if (!JFCComponent.isKnown(parent.component))
+        		throw new AssertionError("Referenced parent is not known.");
+        	builder.append(" parent=\"" + Integer.toHexString(parent.component.hashCode()) + "\"");
+        }
+        builder.append(">"+ StringTools.ENDLINE);
+        builder.append(" <param name=\"title\" value=\"" + title + "\" />" + StringTools.ENDLINE);
+        builder.append(" <param name=\"class\" value=\"" + componentClass + "\" />" +
+            StringTools.ENDLINE);
+        builder.append(" <param name=\"icon\" value=\"" + icon + "\" />" + StringTools.ENDLINE);
+        builder.append(" <param name=\"index\" value=\"" + index + "\" />" + StringTools.ENDLINE);
+        builder.append(" <param name=\"hash\" value=\"" +
+            Integer.toHexString(component.hashCode()) + "\" />" + StringTools.ENDLINE);
+        builder.append(getInheritanceTree());
+        builder.append("</component>" + StringTools.ENDLINE);
+        return builder.toString();
+    }
+    
+    /**
+     * <p>
+     * Returns an XML representation of the components children.
+     * </p>
+     * @return XML representation of children
+     */
+    public String printChildren(){
+    	StringBuilder builder = new StringBuilder();
+        for (JFCComponent child: children){
+            	builder.append(child.getXML());
+            	builder.append(child.printChildren());
+        }
+    	return builder.toString();
+    }
+
+    /**
+     * <p>
+     * Removes a child component from the current component.
+     * </p>
+     * 
+     * @param child
+     *            child component to be removed
+     */
+    private void removeChild(JFCComponent child) {
+        children.remove(child);
+    }
+
+    /**
+     * <p>
+     * Removes the component from the list of children of its parent.
+     * </p>
+     */
+    private void removeFromParent() {
+        if (parent != null) {
+            parent.removeChild(this);
+        }
+    }
+
+    /**
+     * <p>
+     * Triggers the removals of all child components from the GUI hierarchy, i.e., calls
+     * {@link #remove(Component)} for all child components.
+     * </p>
+     */
+    private void removeChildren() {
+        for (JFCComponent child : children) {
+            remove(child.component);
+        }
+    }
+
+    /**
+     * <p>
+     * Sets the {@link #title} of the component. The title is defined as follows (first in the list,
+     * that is not null):
+     * <ul>
+     * <li>accessible name of the component if available</li>
+     * <li>{@link #icon} of the component</li>
+     * <li>name of the component</li>
+     * <li>coordinates of the component</li>
+     * </ul>
+     * </p>
+     */
+    private void setTitle() {
+        title = null; // reset title
+
+        AccessibleContext accessibleContext = component.getAccessibleContext();
+        if (accessibleContext != null) {
+            title = accessibleContext.getAccessibleName();
+        }
+        if (title == null) {
+            title = icon;
+        }
+        if (title == null) {
+            title = component.getName();
+        }
+        if (title == null) {
+            // use coordinates as last resort
+            title = "Pos(" + component.getX() + "," + component.getY() + ")";
+        }
+    }
+
+    /**
+     * <p>
+     * Sets the {@link #componentClass} of the component.
+     * </p>
+     */
+    private void setClass() {
+        componentClass = component.getClass().getName();
+    }
+
+    /**
+     * <p>
+     * Sets the {@link #icon} of the component.
+     * </p>
+     */
+    private void setIcon() {
+        icon = null; // reset icon
+
+        Method getIconMethod;
+        try {
+            getIconMethod = component.getClass().getMethod("getIcon", new Class[0]);
+            if (getIconMethod != null) {
+                Object iconObject = getIconMethod.invoke(component, new Object[] { });
+                if (iconObject != null) {
+                    String iconPath = iconObject.toString();
+                    if (!iconPath.contains("@")) {
+                        System.out.println("iconPath");
+                        String[] splitResult =
+                            iconPath.split(File.separatorChar == '\\' ? "\\\\" : File.separator);
+                        icon = splitResult[splitResult.length - 1];
+                    }
+                }
+            }
+        }
+        catch (SecurityException e) {}
+        catch (NoSuchMethodException e) {}
+        catch (IllegalArgumentException e) {}
+        catch (IllegalAccessException e) {}
+        catch (InvocationTargetException e) {
+            System.err.println("Found method with name " + "getIcon" + " but could not access it.");
+        }
+    }
+
+    /**
+     * <p>
+     * Sets the {@link #index} of the component as the index in the parent, if it is accessible.
+     * </p>
+     */
+    private void setIndex() {
+        index = -1; // reset index
+
+        AccessibleContext accessibleContext = component.getAccessibleContext();
+        if (accessibleContext != null) {
+            index = accessibleContext.getAccessibleIndexInParent();
+        }
+    }
+    
+    /**
+     * <p>
+     * Constructs a string that represents inheritance of component.
+     * </p>
+     * @return
+     */
+    private String getInheritanceTree(){
+    	StringBuilder builder = new StringBuilder();
+    	Class<? extends Object> classobject = component.getClass();
+    	while(classobject.getSuperclass() != null){
+    		classobject = classobject.getSuperclass();
+    		builder.append(" <ancestor name=\"");
+    		builder.append(classobject.getName());
+    		builder.append("\" />" + StringTools.ENDLINE);
+    	}
+    	return builder.toString();
+    }
+}
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCContainerListener.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCContainerListener.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCContainerListener.java	(revision 922)
@@ -0,0 +1,53 @@
+
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.awt.Component;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+
+/**
+ * <p>
+ * A ComponentListener responsible for monitoring adding and removing of GUI components
+ * </p>
+ * 
+ * @author Fabian Glaser
+ * @version 1.0
+ */
+public class JFCContainerListener implements ContainerListener {
+	 /**
+     * <p>
+     * Writer for logging events.
+     * </p>
+     */
+    final private JFCMonitorOutputWriter outputWriter;	
+	
+    /**
+     * <p>
+     * Constructor. Creates a new JFCContainerListener with a given {@link JFCMonitorOutputWriter}, where the
+     * monitored information is logged.
+     * </p>
+     * 
+     * @param outputWriter
+     *            writer for the logged information
+     */
+    public JFCContainerListener(JFCMonitorOutputWriter outputWriter) {
+        this.outputWriter = outputWriter;
+    }
+
+	@Override
+	public void componentAdded(ContainerEvent e) {
+		Component src = e.getChild();
+		if (!JFCComponent.isKnown(src)){
+			JFCComponent.add(src);
+			JFCComponent jfcComponent = JFCComponent.find(src);
+			outputWriter.write(jfcComponent.getXML());
+			outputWriter.write(jfcComponent.printChildren());
+		}		
+	}
+
+	@Override
+	public void componentRemoved(ContainerEvent e) {
+		//System.out.println("Component has been removed...");
+	}
+
+}
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCListener.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCListener.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCListener.java	(revision 922)
@@ -0,0 +1,133 @@
+
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.event.AWTEventListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * This class implements monitoring of AWT and Swing mouse and keyboard events. Each of the events
+ * is written to an output stream.
+ * </p>
+ * 
+ * @author Steffen Herbold 
+ * @author Fabian Glaser
+ * @version 1.0
+ */
+public class JFCListener implements AWTEventListener {
+
+    /**
+     * <p>
+     * Writer for logging events.
+     * </p>
+     */
+    final private JFCMonitorOutputWriter outputWriter;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCListener with a given {@link JFCMonitorOutputWriter}, where the
+     * monitored information is logged.
+     * </p>
+     * 
+     * @param outputWriter
+     *            writer for the logged information
+     */
+    public JFCListener(JFCMonitorOutputWriter outputWriter) {
+        this.outputWriter = outputWriter;
+    }
+
+    /**
+     * <p>
+     * Writes all received {@link MouseEvent}s and {@link KeyEvent}s to the {@link #outputWriter}.
+     * </p>
+     * 
+     * @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent)
+     */
+    @Override
+    public void eventDispatched(AWTEvent event) {
+        StringBuilder builder = new StringBuilder();
+
+        if (event instanceof MouseEvent) {
+            if (!isMouseMovement(event.getID())) {
+                MouseEvent mouseEvent = (MouseEvent) event;
+                builder.append("<event id=\"" + event.getID() + "\">" + StringTools.ENDLINE);
+                builder.append(" <param name=\"X\" value=\"" + mouseEvent.getX() + "\" />" +
+                    StringTools.ENDLINE);
+                builder.append(" <param name=\"Y\" value=\"" + mouseEvent.getY() + "\" />" +
+                    StringTools.ENDLINE);
+                builder.append(" <param name=\"Button\" value=\"" + mouseEvent.getButton() +
+                    "\" />" + StringTools.ENDLINE);
+                builder.append(" <param name=\"Modifiers\" value=\"" + mouseEvent.getModifiers() +
+                    "\" />" + StringTools.ENDLINE);
+                addSourceInfo(builder, event);
+                builder.append("</event>" + StringTools.ENDLINE);
+            }
+        }
+        else if (event instanceof KeyEvent) {
+            if (event.getID() == KeyEvent.KEY_PRESSED || event.getID() == KeyEvent.KEY_RELEASED) {
+                KeyEvent keyEvent = (KeyEvent) event;
+                builder.append("<event id=\"" + event.getID() + "\">" + StringTools.ENDLINE);
+                builder.append(" <param name=\"KeyCode\" value=\"" + keyEvent.getKeyCode() +
+                    "\" />" + StringTools.ENDLINE);
+                builder.append(" <param name=\"Modifiers\" value=\"" + keyEvent.getModifiers() +
+                    "\" />" + StringTools.ENDLINE);
+                addSourceInfo(builder, event);
+                builder.append("</event>" + StringTools.ENDLINE);
+            }
+        }
+        else if (event instanceof FocusEvent) {
+            if (event.getID() == FocusEvent.FOCUS_GAINED) {
+                builder.append("<event id=\"" + event.getID() + "\">" + StringTools.ENDLINE);
+                addSourceInfo(builder, event);
+                builder.append("</event>" + StringTools.ENDLINE);
+            }
+        }
+        if (builder.length() > 0 && outputWriter != null) {
+            outputWriter.write(builder.toString());
+        }
+    }
+
+    /**
+     * <p>
+     * Appends information about the event to a {@link StringBuilder}.
+     * </p>
+     * 
+     * @param builder
+     *            {@link StringBuilder} where the information is appended
+     * @param event
+     *            event whose information is appended
+     */
+    private void addSourceInfo(StringBuilder builder, AWTEvent event) {
+        builder.append(" <source");
+        if (event.getSource() instanceof Component) {
+            Component source = (Component) event.getSource();
+            builder.append(" hash=\"" + Integer.toHexString(source.hashCode()) + "\"");
+        }  
+        builder.append(">" + StringTools.ENDLINE);
+        builder.append("  <param name=\"toString\" value=\"" +
+            StringTools.xmlEntityReplacement(event.getSource().toString()) + "\" />" +
+            StringTools.ENDLINE);
+        builder.append(" </source>" + StringTools.ENDLINE);
+    }
+
+    /**
+     * <p>
+     * Checks if the Id of an {@link AWTEvent} is a mouse movement Id.
+     * </p>
+     * 
+     * @param eventId
+     *            id of the {@link AWTEvent}
+     * @return true, if the event is a mouse movement event; false otherwise
+     */
+    private boolean isMouseMovement(int eventId) {
+        return eventId == MouseEvent.MOUSE_MOVED || eventId == MouseEvent.MOUSE_DRAGGED ||
+            eventId == MouseEvent.MOUSE_ENTERED || eventId == MouseEvent.MOUSE_EXITED;
+    }
+
+}
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCMonitorOutputWriter.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCMonitorOutputWriter.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCMonitorOutputWriter.java	(revision 922)
@@ -0,0 +1,54 @@
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import de.ugoe.cs.util.StringTools;
+/**
+ * <p>
+ * This class is used as wrapper for writers for JFCMonitor logs. 
+ * </p>
+ * @author Fabian Glaser
+ * @version 1.0
+ *
+ */
+public class JFCMonitorOutputWriter{
+	 /**
+     * <p>
+     * Writer for logging events.
+     * </p>
+     */
+    final private OutputStreamWriter outputWriter;
+    
+    /**
+     * <p>
+     * Constructor. Creates a new JFCMonitorOutputWriter with a given {@link OutputStreamWriter}, where the
+     * monitored information is written to. It writes log header information on construction to 
+     * given OutputStreamWriter.  
+     * </p>
+     * 
+     * @param outputWriter
+     *            writer for the logged information
+     */
+    
+    public JFCMonitorOutputWriter(OutputStreamWriter outputWriter) {
+        this.outputWriter = outputWriter;
+        write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + StringTools.ENDLINE);
+        write("<sessions>" + StringTools.ENDLINE);
+    }
+    
+    /**
+     * <p>
+     * Writes a string to the internal OutputStreamWriter.
+     * </p>
+     * @param str
+     */
+    public synchronized void write(String str){
+    	try {
+			outputWriter.write(str);
+			outputWriter.flush();
+		} catch (IOException e) {
+	        System.err.println("JFCMONITOR -- Failure writing to log: " + e.getMessage());
+		}
+    }
+}
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCNameChangeListener.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCNameChangeListener.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCNameChangeListener.java	(revision 922)
@@ -0,0 +1,73 @@
+/**
+ * 
+ */
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.awt.Component;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.AccessibleContext;
+
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * Listener that listens on the name change of a component.	
+ * </p>
+ * @author Fabian Glaser
+ * @version 1.0
+ */
+public class JFCNameChangeListener implements PropertyChangeListener {
+	 /**
+     * <p>
+     * Writer for logging events.
+     * </p>
+     */
+    final private JFCMonitorOutputWriter outputWriter;
+    
+    /**
+     * <p>
+     * Constructor. Creates a new JFCNameChangeListener with a given 
+     * {@link JFCMonitorOutputWriter}.	
+     * </p>
+     * @param outputWriter
+     */
+    public JFCNameChangeListener(JFCMonitorOutputWriter outputWriter){
+    	this.outputWriter = outputWriter;
+    }
+	
+	@Override
+	public void propertyChange(PropertyChangeEvent evt) {
+		String propertyName = evt.getPropertyName();
+		Component component = null;
+		
+		if (propertyName.equals("AccessibleName")){
+			AccessibleContext context = (AccessibleContext) evt.getSource();
+			component = (Component) context.getAccessibleParent();
+		}
+		
+		if (propertyName.equals("name")){
+			component = (Component) evt.getSource();
+		}
+		
+		if (propertyName.equals("")){
+			
+		}
+		
+		if (component != null){
+			if (!JFCComponent.isKnown(component)){
+				System.err.println("Referenced component is not known");
+				throw new AssertionError("Referenced component is not known.");
+			}
+			StringBuilder builder = new StringBuilder();
+			builder.append("<componentNameChange hash=\"");
+			builder.append(Integer.toHexString(component.hashCode()));
+			builder.append("\" newName=\"" + evt.getNewValue());
+			builder.append("\" source=\"" + propertyName);
+			builder.append("\" />" + StringTools.ENDLINE);
+			outputWriter.write(builder.toString());
+		}
+	}
+	
+}
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCWindowMonitor.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCWindowMonitor.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JFCWindowMonitor.java	(revision 922)
@@ -0,0 +1,68 @@
+
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.awt.AWTEvent;
+import java.awt.Window;
+import java.awt.event.AWTEventListener;
+import java.awt.event.WindowEvent;
+
+/**
+ * <p>
+ * An AWT event listener responsible to monitor the window creation and destruction.
+ * </p>
+ * 
+ * @author Steffen Herbold 
+ * @author Fabian Glaser
+ * @version 1.0
+ */
+public class JFCWindowMonitor implements AWTEventListener {
+	 /**
+     * <p>
+     * Writer for logging events.
+     * </p>
+     */
+    final private JFCMonitorOutputWriter outputWriter;	
+	
+    /**
+     * <p>
+     * Constructor. Creates a new JFCWindowMonitor with a given {@link JFCMonitorOutputWriter}, where the
+     * monitored information is logged.
+     * </p>
+     * 
+     * @param outputWriter
+     *            writer for the logged information
+     */
+    public JFCWindowMonitor(JFCMonitorOutputWriter outputWriter) {
+        this.outputWriter = outputWriter;
+    }
+	
+
+    /**
+     * <p>
+     * Adds all created windows (and their child components) to the GUI hierarchy maintained by
+     * {@link JFCComponent} and removes them if a window is destroyed.
+     * </p>
+     * </p>
+     * 
+     * @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent)
+     */
+    @Override
+    public void eventDispatched(AWTEvent event) {
+        Window window;
+        switch (event.getID())
+        {
+            case WindowEvent.WINDOW_OPENED:
+                window = ((WindowEvent) event).getWindow();
+                JFCComponent.add(window);
+                JFCComponent jfcComponent = JFCComponent.find(window);
+                if (jfcComponent != null) {
+                    outputWriter.write(jfcComponent.getXML());
+                    outputWriter.write(jfcComponent.printChildren());
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+}
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JarLauncher.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JarLauncher.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JarLauncher.java	(revision 922)
@@ -0,0 +1,271 @@
+
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+/**
+ * <p>
+ * Class that launches an executable Jar-file in the same thread and VM where the JarLauncher
+ * instance is created. The requirements on the Jar file are:
+ * <li>Must contain a MANIFEST.</li>
+ * <li>The MANIFEST must define the main-class of the application ("Main-Class" entry).</li>
+ * <li>The MANIFEST must define the classpath of the application ("Class-Path" entry).</li>
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class JarLauncher {
+
+    /**
+     * <p>
+     * Name of the Jar file to be executed.
+     * </p>
+     */
+    private String jarfile;
+
+    /**
+     * <p>
+     * Arguments for launching the Jar file.
+     * </p>
+     */
+    private String[] args;
+
+    /**
+     * <p>
+     * Helper variable with the path to the working directory.
+     * </p>
+     */
+    final String workingDir = System.getProperty("user.dir") + "/";
+
+    /**
+     * <p>
+     * Internal variable used to store the classpath extracted from the Jar file's MANIFEST.
+     * </p>
+     */
+    private String[] classPath = new String[] { };
+
+    /**
+     * <p>
+     * Internal variable used to store the name (including package information) of the Jar file's
+     * main function extracted from the Jar file's MANIFEST.
+     * </p>
+     */
+    private String mainClassName = "";
+
+    /**
+     * <p>
+     * Inner class that defines an exception that is thrown if launching the application in the Jar
+     * file fails.
+     * </p>
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    private static class JarLaunchException extends Exception {
+
+        /**
+         * <p>
+         * Id for object serialization.
+         * </p>
+         */
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * <p>
+         * Constructor. Creates a new JarLaunchException.
+         * </p>
+         * 
+         * @param string
+         *            error message of the exception
+         */
+        public JarLaunchException(String string) {
+            super(string);
+        }
+
+        /**
+         * <p>
+         * Constructor. Creates a new JarLaunchException as a copy of an existing exception.
+         * </p>
+         * 
+         * @param e
+         *            exception that is copied
+         */
+        public JarLaunchException(Exception e) {
+            super(e);
+        }
+
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new JarLauncher.
+     * </p>
+     * 
+     * @param jarfile
+     *            file to be launched; must not be complete path but in relation to the current
+     *            working directory
+     * @param args
+     *            arguments with which the main function of the Jar file is called
+     */
+    public JarLauncher(String jarfile, String[] args) {
+        this.jarfile = jarfile;
+        this.args = Arrays.copyOf(args, args.length);
+    }
+
+    /**
+     * <p>
+     * Executes the main function of the Jar file associated with this launcher.
+     * </p>
+     */
+    public void exec() {
+        try {
+            getInfoFromJar();
+            initClassLoader();
+            runMain();
+        }
+        catch (JarLaunchException e) {
+            System.err.println("Failure to launch application.");
+            System.err.println(e.getMessage());
+        }
+    }
+
+    /**
+     * <p>
+     * Retrieves the classpath and main function from the Jar file's MANIFEST.
+     * </p>
+     * 
+     * @throws JarLaunchException
+     *             thrown if reading of Jar file or MANIFEST fails
+     */
+    private void getInfoFromJar() throws JarLaunchException {
+        JarInputStream jarInputStream;
+        try {
+            jarInputStream = new JarInputStream(new FileInputStream(workingDir + jarfile));
+        }
+        catch (FileNotFoundException e) {
+            throw new JarLaunchException(e);
+        }
+        catch (IOException e) {
+            throw new JarLaunchException(e);
+        }
+        Manifest manifest = jarInputStream.getManifest();
+        mainClassName = manifest.getMainAttributes().getValue("Main-Class");
+        String jarClassPath = manifest.getMainAttributes().getValue("Class-Path");
+        String[] jarClassPathElements = jarClassPath.split(" ");
+        classPath = new String[jarClassPathElements.length];
+        for (int i = 0; i < jarClassPathElements.length; i++) {
+            classPath[i] = "file:" + workingDir + jarClassPathElements[i];
+        }
+        try {
+            jarInputStream.close();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * <p>
+     * Modifies the {@link ClassLoader} of the current VM such that it includes the class path
+     * defined in the Jar file's MANIFEST.
+     * </p>
+     * 
+     * @throws JarLaunchException
+     *             thrown if modification of {@link ClassLoader} fails.
+     */
+    private void initClassLoader() throws JarLaunchException {
+        URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
+        Method method;
+        try {
+            method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]
+                { URL.class });
+        }
+        catch (SecurityException e) {
+            throw new JarLaunchException(
+                                         "addURL method of URLClassLoader not accessible via reflection.");
+        }
+        catch (NoSuchMethodException e) {
+            throw new JarLaunchException(
+                                         "URLClassLoader does not have addURL method. Should be impossible!!");
+        }
+        method.setAccessible(true);
+
+        try {
+            method.invoke(classLoader, new Object[]
+                { new URL("file:" + workingDir + jarfile) });
+            for (String element : classPath) {
+                method.invoke(classLoader, new Object[]
+                    { new URL(element) });
+            }
+        }
+        catch (IllegalArgumentException e) {
+            throw new JarLaunchException(
+                                         "Illegal arguments for addURL method. Should be impossible!!");
+        }
+        catch (MalformedURLException e) {
+            throw new JarLaunchException(e);
+        }
+        catch (IllegalAccessException e) {
+            throw new JarLaunchException(
+                                         "addURL method of URLClassLoader not accessible via reflection.");
+        }
+        catch (InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * <p>
+     * Executes the main function.
+     * </p>
+     * 
+     * @throws JarLaunchException
+     *             thrown if execution of main function fails or the main function itself throws an
+     *             exception
+     */
+    private void runMain() throws JarLaunchException {
+        Class<?> mainClass;
+        try {
+            mainClass = Class.forName(mainClassName);
+        }
+        catch (ClassNotFoundException e) {
+            throw new JarLaunchException("Main class not found: " + mainClassName);
+        }
+        Method mainMethod;
+        try {
+            mainMethod = mainClass.getMethod("main", new Class[]
+                { String[].class });
+        }
+        catch (SecurityException e) {
+            throw new JarLaunchException("Main method not accessible.");
+        }
+        catch (NoSuchMethodException e) {
+            throw new JarLaunchException("Main method not found.");
+        }
+        try {
+            mainMethod.invoke(null, new Object[]
+                { args });
+        }
+        catch (IllegalArgumentException e) {
+            throw new JarLaunchException(
+                                         "Illegal arguments for main method. Should be impossible!!");
+        }
+        catch (IllegalAccessException e) {
+            throw new JarLaunchException("Main method not accessible.");
+        }
+        catch (InvocationTargetException e) {
+            throw new JarLaunchException(e);
+        }
+    }
+}
Index: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/Runner.java
===================================================================
--- trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/Runner.java	(revision 922)
+++ trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/Runner.java	(revision 922)
@@ -0,0 +1,96 @@
+
+package de.ugoe.cs.autoquest.jfcmonitor;
+
+import java.awt.AWTEvent;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.awt.event.FocusEvent;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+/**
+ * <p>
+ * Start-up class of the application.
+ * </p>
+ * <p>
+ * Launches the application under test in the same JVM.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @author Fabian Glaser
+ * @version 1.0
+ */
+public class Runner {
+
+    /**
+     * <p>
+     * Debugging variable. If set to true, the logging is also written to the console.
+     * </p>
+     */
+    private final static boolean stdOutputWrite = true;
+
+    /**
+     * <p>
+     * Main method of the application.
+     * </p>
+     * 
+     * @param args
+     *            the first parameter defines the Jar file that contains the start-up information of
+     *            the application under test. The remaining parameters are passed on the toe
+     *            application under test.
+     */
+    public static void main(String[] args) {
+        String logfileName = "jfcmonitor_" + System.currentTimeMillis() + ".log";
+
+        FileOutputStream fis;
+        OutputStreamWriter writer;
+        try {
+            // the writer is not closed explicitly!
+            fis = new FileOutputStream(logfileName, true);
+            writer = new OutputStreamWriter(fis, "UTF-8");
+        }
+        catch (IOException e) {
+            System.err.println("JFCMONITOR -- failure opening logfile: " + e.getMessage());
+            return;
+        }
+
+        JFCMonitorOutputWriter jfcWriter = new JFCMonitorOutputWriter(writer);
+        AWTEventListener listenerFile = new JFCListener(jfcWriter);
+        Toolkit.getDefaultToolkit().addAWTEventListener(listenerFile, AWTEvent.KEY_EVENT_MASK);
+        Toolkit.getDefaultToolkit().addAWTEventListener(listenerFile, AWTEvent.MOUSE_EVENT_MASK);
+        Toolkit.getDefaultToolkit().addAWTEventListener(listenerFile, FocusEvent.FOCUS_EVENT_MASK);
+        Toolkit.getDefaultToolkit().addAWTEventListener(new JFCWindowMonitor(jfcWriter),
+                                                        AWTEvent.WINDOW_EVENT_MASK);
+        JFCComponent.addPropertyChangeListener(new JFCNameChangeListener(jfcWriter));
+        JFCComponent.addContainerListener(new JFCContainerListener(jfcWriter));
+
+        if (stdOutputWrite) {
+            AWTEventListener listenerStdOut;
+            try {
+            	OutputStreamWriter stdOutputWriter = new OutputStreamWriter(System.out, "UTF-8");
+            	JFCMonitorOutputWriter stdJfcWriter = new JFCMonitorOutputWriter(stdOutputWriter);
+                listenerStdOut = new JFCListener(stdJfcWriter);
+                Toolkit.getDefaultToolkit().addAWTEventListener(listenerStdOut,
+                                                                AWTEvent.KEY_EVENT_MASK);
+                Toolkit.getDefaultToolkit().addAWTEventListener(listenerStdOut,
+                                                                AWTEvent.MOUSE_EVENT_MASK);
+                Toolkit.getDefaultToolkit().addAWTEventListener(listenerStdOut,
+                                                                FocusEvent.FOCUS_EVENT_MASK);
+                Toolkit.getDefaultToolkit().addAWTEventListener(new JFCWindowMonitor(stdJfcWriter),
+                        AWTEvent.WINDOW_EVENT_MASK);
+                JFCComponent.addPropertyChangeListener(new JFCNameChangeListener(stdJfcWriter));
+                JFCComponent.addContainerListener(new JFCContainerListener(stdJfcWriter));
+            }
+            catch (UnsupportedEncodingException e) {
+                System.err
+                    .println("JFCMONITOR -- failure to create OutputStreamWriter with UTF-8 encoding to System.out");
+            }
+        }
+
+        JarLauncher launcher = new JarLauncher(args[0], Arrays.copyOfRange(args, 1, args.length));
+        launcher.exec();
+    }
+}
Index: trunk/autoquest-misc-test/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-misc-test/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-misc-test/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-misc-test/src/test/java/de/ugoe/cs/quest/keyboardmaps/KeyboardMapTest.java
===================================================================
--- trunk/autoquest-misc-test/src/test/java/de/ugoe/cs/quest/keyboardmaps/KeyboardMapTest.java	(revision 922)
+++ trunk/autoquest-misc-test/src/test/java/de/ugoe/cs/quest/keyboardmaps/KeyboardMapTest.java	(revision 922)
@@ -0,0 +1,407 @@
+package de.ugoe.cs.quest.keyboardmaps;
+
+import static org.junit.Assert.*;
+
+import java.util.Locale;
+import java.util.logging.Level;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.keyboardmaps.KeyboardMap;
+import de.ugoe.cs.autoquest.keyboardmaps.KeyboardMapFactory;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 11.07.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class KeyboardMapTest {
+
+    /**
+     *
+     */
+    @Before
+    public void setUp() {
+        new TextConsole(Level.FINEST);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testInitialization() {
+        KeyboardMap map = KeyboardMapFactory.createKeyboardMap(Locale.ENGLISH);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.FRENCH);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.GERMAN);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.ITALIAN);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.JAPANESE);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.KOREAN);
+        assertNotNull(map);
+
+        try {
+            map = KeyboardMapFactory.createKeyboardMap(Locale.CHINESE);
+            fail("expected exception did not occur");
+        }
+        catch (IllegalArgumentException e) {
+            // was expected and is ignored
+        }
+
+        try {
+            map = KeyboardMapFactory.createKeyboardMap(Locale.SIMPLIFIED_CHINESE);
+            fail("expected exception did not occur");
+        }
+        catch (IllegalArgumentException e) {
+            // was expected and is ignored
+        }
+
+        try {
+            map = KeyboardMapFactory.createKeyboardMap(Locale.TRADITIONAL_CHINESE);
+            fail("expected exception did not occur");
+        }
+        catch (IllegalArgumentException e) {
+            // was expected and is ignored
+        }
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.FRANCE);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.GERMANY);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.ITALY);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.JAPAN);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.KOREA);
+        assertNotNull(map);
+
+        try {
+            map = KeyboardMapFactory.createKeyboardMap(Locale.CHINA);
+            fail("expected exception did not occur");
+        }
+        catch (IllegalArgumentException e) {
+            // was expected and is ignored
+        }
+
+        try {
+            map = KeyboardMapFactory.createKeyboardMap(Locale.PRC);
+            fail("expected exception did not occur");
+        }
+        catch (IllegalArgumentException e) {
+            // was expected and is ignored
+        }
+
+        try {
+            map = KeyboardMapFactory.createKeyboardMap(Locale.TAIWAN);
+            fail("expected exception did not occur");
+        }
+        catch (IllegalArgumentException e) {
+            // was expected and is ignored
+        }
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.UK);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.US);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.CANADA);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.CANADA_FRENCH);
+        assertNotNull(map);
+
+        map = KeyboardMapFactory.createKeyboardMap(Locale.GERMAN);
+        assertNotNull(map);
+
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    @Test
+    public void testGermanKeyboardMap() {
+        KeyboardMap map = KeyboardMapFactory.createKeyboardMap(Locale.GERMAN);
+        assertNotNull(map);
+
+        assertCombinations(map, VirtualKey.DIGIT_1, '1', '!', '¹', '¡', true);
+        assertCombinations(map, VirtualKey.EXCLAMATION_MARK, '1', '!', '¹', '¡', true);
+        assertCombinations(map, VirtualKey.INVERTED_EXCLAMATION_MARK, '1', '!', '¹', '¡', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_2, '2', '"', '²', '⅛', true);
+        assertCombinations(map, VirtualKey.QUOTEDBL, '2', '"', '²', '⅛', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_3, '3', '§', '³', '£', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_4, '4', '$', '¼', '¤', true);
+        assertCombinations(map, VirtualKey.DOLLAR, '4', '$', '¼', '¤', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_5, '5', '%', '½', '⅜', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_6, '6', '&', '¾', '⅝', true);
+        assertCombinations(map, VirtualKey.AMPERSAND, '6', '&', '¾', '⅝', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_7, '7', '/', '{', '⅞', true);
+        assertCombinations(map, VirtualKey.SLASH, '7', '/', '{', '⅞', true);
+        assertCombinations(map, VirtualKey.BRACELEFT, '7', '/', '{', '⅞', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_8, '8', '(', '[', '™', true);
+        assertCombinations(map, VirtualKey.LEFT_PARENTHESIS, '8', '(', '[', '™', true);
+        assertCombinations(map, VirtualKey.OPEN_BRACKET, '8', '(', '[', '™', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_9, '9', ')', ']', '±', true);
+        assertCombinations(map, VirtualKey.RIGHT_PARENTHESIS, '9', ')', ']', '±', true);
+        assertCombinations(map, VirtualKey.CLOSE_BRACKET, '9', ')', ']', '±', true);
+
+        assertCombinations(map, VirtualKey.DIGIT_0, '0', '=', '}', 0, true);
+        assertCombinations(map, VirtualKey.EQUALS, '0', '=', '}', 0, true);
+        assertCombinations(map, VirtualKey.BRACERIGHT, '0', '=', '}', 0, true);
+
+        assertCombinations(map, VirtualKey.BACK_SLASH, 'ß', '?', '\\', '¿', true);
+
+        assertCombinations(map, VirtualKey.LETTER_Q, 'q', 'Q', '@', 'Ω', true);
+        assertCombinations(map, VirtualKey.AT, 'q', 'Q', '@', 'Ω', true);
+
+        assertCombinations(map, VirtualKey.LETTER_W, 'w', 'W', 0, 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_E, 'e', 'E', '€', 0, true);
+        assertCombinations(map, VirtualKey.EURO_SIGN, 'e', 'E', '€', 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_R, 'r', 'R', 0, '®', true);
+
+        assertCombinations(map, VirtualKey.LETTER_T, 't', 'T', 'ŧ', 'Ŧ', true);
+
+        assertCombinations(map, VirtualKey.LETTER_Z, 'z', 'Z', '←', '¥', true);
+
+        assertCombinations(map, VirtualKey.LETTER_U, 'u', 'U', '↓', '↑', true);
+
+        assertCombinations(map, VirtualKey.LETTER_I, 'i', 'I', '→', 'ı', true);
+
+        assertCombinations(map, VirtualKey.LETTER_O, 'o', 'O', 'ø', 'Ø', true);
+
+        assertCombinations(map, VirtualKey.LETTER_P, 'p', 'P', 'þ', 'Þ', true);
+
+        assertCombinations(map, VirtualKey.LETTER_A, 'a', 'A', 'æ', 'Æ', true);
+
+        assertCombinations(map, VirtualKey.LETTER_S, 's', 'S', 0, 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_D, 'd', 'D', 'ð', 'Ð', true);
+
+        assertCombinations(map, VirtualKey.LETTER_F, 'f', 'F', 'đ', 'ª', true);
+
+        assertCombinations(map, VirtualKey.LETTER_G, 'g', 'G', 'ŋ', 'Ŋ', true);
+
+        assertCombinations(map, VirtualKey.LETTER_H, 'h', 'H', 'ħ', 'Ħ', true);
+
+        assertCombinations(map, VirtualKey.LETTER_J, 'j', 'J', 0, 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_K, 'k', 'K', 'ĸ', 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_L, 'l', 'L', 0, 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_Y, 'y', 'Y', '»', 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_X, 'x', 'X', '«', 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_C, 'c', 'C', '¢', '©', true);
+
+        assertCombinations(map, VirtualKey.LETTER_V, 'v', 'V', '„', 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_B, 'b', 'B', '“', 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_N, 'n', 'N', 0, 0, true);
+
+        assertCombinations(map, VirtualKey.LETTER_M, 'm', 'M', 'µ', 'º', true);
+
+        assertCombinations(map, VirtualKey.NUMPAD_0, '0', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_1, '1', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_2, '2', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_3, '3', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_4, '4', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_5, '5', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_6, '6', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_7, '7', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_8, '8', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.NUMPAD_9, '9', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.SEPARATOR, ',', 0, 0, 0, false);
+        assertCombinations(map, VirtualKey.DECIMAL, ',', 0, 0, 0, false);
+
+        assertCombinations(map, VirtualKey.CIRCUMFLEX, '^', '°', '¬', 0, true);
+
+        assertCombinations(map, VirtualKey.TAB, '\t', 0, '\t', 0, true);
+
+        assertCombinations(map, VirtualKey.SPACE, ' ', ' ', ' ', ' ', true);
+
+        assertCombinations(map, VirtualKey.COMMA, ',', ';', '·', '×', true);
+        assertCombinations(map, VirtualKey.SEMICOLON, ',', ';', '·', '×', true);
+        assertCombinations(map, VirtualKey.MULTIPLY, ',', ';', '·', '×', true);
+
+        assertCombinations(map, VirtualKey.MINUS, '-', '_', 0, 0, true);
+        assertCombinations(map, VirtualKey.UNDERSCORE, '-', '_', 0, 0, true);
+
+        assertCombinations(map, VirtualKey.PERIOD, '.', ':', '…', '÷', true);
+        assertCombinations(map, VirtualKey.COLON, '.', ':', '…', '÷', true);
+        assertCombinations(map, VirtualKey.DIVIDE, '.', ':', '…', '÷', true);
+
+        assertCombinations(map, VirtualKey.PLUS, '+', '*', '~', 0, true);
+        assertCombinations(map, VirtualKey.ASTERISK, '+', '*', '~', 0, true);
+        assertCombinations(map, VirtualKey.DEAD_TILDE, '+', '*', '~', 0, true);
+
+        assertCombinations(map, VirtualKey.LESS, '<', '>', '|', '¦', true);
+        assertCombinations(map, VirtualKey.GREATER, '<', '>', '|', '¦', true);
+
+        assertCombinations(map, VirtualKey.NUMBER_SIGN, '#', '\'', 0, 0, true);
+
+        /*
+         * DEAD_GRAVE(KeyEvent.VK_DEAD_GRAVE), DEAD_ACUTE(KeyEvent.VK_DEAD_ACUTE),
+         * DEAD_CIRCUMFLEX(KeyEvent.VK_DEAD_CIRCUMFLEX), DEAD_TILDE(KeyEvent.VK_DEAD_TILDE),
+         * DEAD_MACRON(KeyEvent.VK_DEAD_MACRON), DEAD_BREVE(KeyEvent.VK_DEAD_BREVE),
+         * DEAD_ABOVEDOT(KeyEvent.VK_DEAD_ABOVEDOT), DEAD_DIAERESIS(KeyEvent.VK_DEAD_DIAERESIS),
+         * DEAD_ABOVERING(KeyEvent.VK_DEAD_ABOVERING),
+         * DEAD_DOUBLEACUTE(KeyEvent.VK_DEAD_DOUBLEACUTE), DEAD_CARON(KeyEvent.VK_DEAD_CARON),
+         * DEAD_CEDILLA(KeyEvent.VK_DEAD_CEDILLA), DEAD_OGONEK(KeyEvent.VK_DEAD_OGONEK),
+         * DEAD_IOTA(KeyEvent.VK_DEAD_IOTA), DEAD_VOICED_SOUND(KeyEvent.VK_DEAD_VOICED_SOUND),
+         * DEAD_SEMIVOICED_SOUND(KeyEvent.VK_DEAD_SEMIVOICED_SOUND),
+         */
+    }
+
+    /**
+   *
+   */
+    private void assertCombinations(KeyboardMap map,
+                                    VirtualKey  key,
+                                    int         normal,
+                                    int         withShift,
+                                    int         withAltGr,
+                                    int         withShiftAndAltGr,
+                                    boolean     numLockIndependent)
+    {
+        if (numLockIndependent) {
+            if (normal != 0) {
+                assertCombination("normal", normal, map, key, false, false, false);
+            }
+
+            if (withShift != 0) {
+                assertCombination("shift", withShift, map, key, false, true, false);
+            }
+
+            if (withAltGr != 0) {
+                assertCombination("altgr", withAltGr, map, key, false, false, true);
+            }
+
+            if (withShiftAndAltGr != 0) {
+                assertCombination("shift and altgr", withShiftAndAltGr, map, key, false, true, true);
+            }
+        }
+        else {
+            assertInvalidCombination("normal", normal, map, key, false, false, false);
+            assertInvalidCombination("shift", withShift, map, key, false, true, false);
+            assertInvalidCombination("altgr", withAltGr, map, key, false, false, true);
+            assertInvalidCombination
+              ("shift and altgr", withShiftAndAltGr, map, key, false, true, true);
+        }
+
+        if (normal != 0) {
+            assertCombination("numlock", normal, map, key, true, false, false);
+        }
+
+        if (withShift != 0) {
+            assertCombination("numlock and shift", withShift, map, key, true, true, false);
+        }
+
+        if (withAltGr != 0) {
+            assertCombination("numlock and altgr", withAltGr, map, key, true, false, true);
+        }
+
+        if (withShiftAndAltGr != 0) {
+            assertCombination
+              ("numlock, shift and altgr", withShiftAndAltGr, map, key, true, true, true);
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param string
+     * @param normal
+     * @param characterFor
+     */
+    private void assertCombination(String      type,
+                                   int         expectedChar,
+                                   KeyboardMap map,
+                                   VirtualKey  key,
+                                   boolean     numlock,
+                                   boolean     shift,
+                                   boolean     altgr)
+    {
+        String message = "checked for " + type + ": expected '" + ((char) expectedChar) + "'";
+
+        char retrievedChar;
+        try {
+            retrievedChar = map.getCharacterFor(key, numlock, shift, altgr, false);
+        }
+        catch (IllegalArgumentException e) {
+            fail("no character found. " + message);
+            return;
+        }
+
+        message += " but got '" + retrievedChar + "'";
+
+        assertEquals(message, expectedChar, retrievedChar);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param string
+     * @param normal
+     * @param characterFor
+     */
+    private void assertInvalidCombination(String      type,
+                                          int         expectedChar,
+                                          KeyboardMap map,
+                                          VirtualKey  key,
+                                          boolean     numlock,
+                                          boolean     shift,
+                                          boolean     altgr)
+    {
+        char retrievedChar;
+        try {
+            retrievedChar = map.getCharacterFor(key, numlock, shift, altgr, false);
+            assertEquals(Character.UNASSIGNED, retrievedChar);
+        }
+        catch (IllegalArgumentException e) {
+            // this is ok and checked for
+        }
+
+    }
+
+}
Index: trunk/autoquest-misc/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-misc/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-misc/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/CommandHelpers.java
===================================================================
--- trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/CommandHelpers.java	(revision 922)
+++ trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/CommandHelpers.java	(revision 922)
@@ -0,0 +1,56 @@
+package de.ugoe.cs.autoquest;
+
+import java.util.logging.Level;
+
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Helper class that collects methods that are often used by the commands.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CommandHelpers {
+
+	/**
+	 * <p>
+	 * Prints a message to error stream of the {@link Console} that an object
+	 * has not been found in the storage.
+	 * </p>
+	 * 
+	 * @param objectName
+	 *            name of the object
+	 */
+	public static void objectNotFoundMessage(String objectName) {
+		Console.printerrln("Object " + objectName + " not found in storage.");
+	}
+
+	/**
+	 * <p>
+	 * Prints a message to the error stream of the {@link Console} that an
+	 * object is not of an expected type.
+	 * </p>
+	 * 
+	 * @param objectName
+	 *            name of the object
+	 * @param type
+	 *            expected type
+	 */
+	public static void objectNotType(String objectName, String type) {
+		Console.printerrln("Object " + objectName + "not of type " + type + ".");
+	}
+
+	/**
+	 * <p>
+	 * Prints a message to the trace stream of the {@link Console} that an
+	 * object in the storage has been overwritten.
+	 * </p>
+	 * 
+	 * @param objectName
+	 */
+	public static void dataOverwritten(String objectName) {
+		Console.traceln(Level.INFO, "Existing object " + objectName + " overwritten.");
+	}
+}
Index: trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyStroke.java
===================================================================
--- trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyStroke.java	(revision 922)
+++ trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyStroke.java	(revision 922)
@@ -0,0 +1,243 @@
+
+package de.ugoe.cs.autoquest.keyboardmaps;
+
+/**
+ * <p>
+ * This class is used to define key strokes.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class KeyStroke {
+
+    /**
+     * <p>
+     * Name of the key stroke.
+     * </p>
+     */
+    private String keyStrokeName;
+
+    /**
+     * <p>
+     * {@link VirtualKey} associated with the key stroke.
+     * </p>
+     */
+    private VirtualKey virtualKey;
+
+    /**
+     * <p>
+     * Defines whether numlock is pressed during the stroke.
+     * </p>
+     */
+    private boolean numlock;
+
+    /**
+     * <p>
+     * Defines whether localstate is pressed during the stroke.
+     * </p>
+     */
+    private boolean localstate;
+
+    /**
+     * <p>
+     * Defines whether shift is pressed during the stroke.
+     * </p>
+     */
+    private boolean shift;
+
+    /**
+     * <p>
+     * Defines whether altgr is pressed during the stroke.
+     * </p>
+     */
+    private boolean altgr;
+
+    /**
+     * <p>
+     * Defines whether inhibit is pressed during the stroke.
+     * </p>
+     */
+    private boolean inhibit;
+
+    /**
+     * <p>
+     * Defines the character in which the key stroke results.
+     * </p>
+     */
+    private char character;
+
+    /**
+     * <p>
+     * Constructor. Creates a new key stroke
+     * </p>
+     * 
+     * @param keyStrokeName
+     *            name of the key stroke
+     * @param virtualKey
+     *            virtual key associated with the key stroke
+     * @param numlock
+     *            defines whether numlock is pressed during the key stroke
+     * @param localstate
+     *            defines whether localstate is pressed during the key stroke
+     * @param shift
+     *            defines whether shift is pressed during the key stroke
+     * @param altgr
+     *            defines whether altgr is pressed during the key stroke
+     * @param inhibit
+     *            defines whether inhibit is pressed during the key stroke
+     * @param character
+     *            defines that character in which the key stroke results
+     */
+    public KeyStroke(String keyStrokeName,
+                     VirtualKey virtualKey,
+                     boolean numlock,
+                     boolean localstate,
+                     boolean shift,
+                     boolean altgr,
+                     boolean inhibit,
+                     char character)
+    {
+        this.keyStrokeName = keyStrokeName;
+        this.virtualKey = virtualKey;
+        this.numlock = numlock;
+        this.localstate = localstate;
+        this.shift = shift;
+        this.altgr = altgr;
+        this.inhibit = inhibit;
+        this.character = character;
+    }
+
+    /**
+     * <p>
+     * Returns the name of the key stroke.
+     * </p>
+     * 
+     * @return the name
+     */
+    public String getKeyStrokeName() {
+        return keyStrokeName;
+    }
+
+    /**
+     * <p>
+     * Returns the virtual key associated with the key stroke.
+     * </p>
+     * 
+     * @return the virtual key
+     */
+    public VirtualKey getVirtualKey() {
+        return virtualKey;
+    }
+
+    /**
+     * <p>
+     * Returns the character in which the key stroke results.
+     * </p>
+     * 
+     * @return the character
+     */
+    public char getCharacter() {
+        return character;
+    }
+
+    /**
+     * <p>
+     * Returns whether inhibit is pressed.
+     * </p>
+     * 
+     * @return true if pressed; false otherwise
+     */
+    public boolean getInhibit() {
+        return inhibit;
+    }
+
+    /**
+     * <p>
+     * Returns whether altgr is pressed.
+     * </p>
+     * 
+     * @return true if pressed; false otherwise
+     */
+    public boolean getAltgr() {
+        return altgr;
+    }
+
+    /**
+     * <p>
+     * Returns whether shift is pressed.
+     * </p>
+     * 
+     * @return true if pressed; false otherwise
+     */
+    public boolean getShift() {
+        return shift;
+    }
+
+    /**
+     * <p>
+     * Returns whether localstate is pressed.
+     * </p>
+     * 
+     * @return true if pressed; false otherwise
+     */
+    public boolean getLocalstate() {
+        return localstate;
+    }
+
+    /**
+     * <p>
+     * Returns whether numlock is pressed.
+     * </p>
+     * 
+     * @return true if pressed; false otherwise
+     */
+    public boolean getNumlock() {
+        return numlock;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuffer toString = new StringBuffer();
+        toString.append("KeyStroke(");
+        toString.append(keyStrokeName);
+        toString.append(", ");
+        toString.append(virtualKey);
+
+        if (character != Character.UNASSIGNED) {
+            toString.append(", \'");
+            toString.append(character);
+            toString.append("\'");
+        }
+
+        if (shift) {
+            toString.append(", shift");
+        }
+
+        if (altgr) {
+            toString.append(", altgr");
+        }
+
+        if (numlock) {
+            toString.append(", numlock");
+        }
+
+        if (localstate) {
+            toString.append(", localstate");
+        }
+
+        if (inhibit) {
+            toString.append(", inhibit");
+        }
+
+        toString.append(")");
+
+        return toString.toString();
+    }
+
+}
Index: trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyboardMap.java
===================================================================
--- trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyboardMap.java	(revision 922)
+++ trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyboardMap.java	(revision 922)
@@ -0,0 +1,2118 @@
+
+package de.ugoe.cs.autoquest.keyboardmaps;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.logging.Level;
+
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Helper class that maps keyboard input based on a key map.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class KeyboardMap {
+
+    /**
+     * <p>
+     * Locale used for the mapping.
+     * </p>
+     */
+    private Locale locale;
+
+    /**
+     * <p>
+     * Name of the mappings-file.
+     * </p>
+     */
+    private String fileName;
+
+    /**
+     * <p>
+     * Map that defines which key strokes define a virtual key.
+     * </p>
+     */
+    private Map<VirtualKey, List<KeyStroke>> keyStrokes =
+        new HashMap<VirtualKey, List<KeyStroke>>();
+
+    /**
+     * <p>
+     * Creates a new KeyboardMap.
+     * </p>
+     * 
+     * @param locale
+     *            Locale that is used for the keyboard map
+     */
+    KeyboardMap(Locale locale) {
+        this.locale = locale;
+
+        if ((this.locale == Locale.ENGLISH) || (this.locale == Locale.US) ||
+            (this.locale == Locale.CANADA))
+        {
+            fileName = "en-us";
+        }
+        else if (locale == Locale.UK) {
+            fileName = "en-gb";
+        }
+        else {
+            fileName = locale.getLanguage();
+        }
+    }
+
+    /**
+     * <p>
+     * Returns a character that is generated, given a {@link VirtualKey} is pressed while the
+     * respective modifiers are active.
+     * </p>
+     * 
+     * @param key
+     *            key that is pressed
+     * @param numlock
+     *            true, if numlock is pressed; false otherwise
+     * @param shift
+     *            true, if shift is pressed; false otherwise
+     * @param altgr
+     *            true, if altgr is pressed; false otherwise
+     * @param inhibit
+     *            true, if inhibit is pressed; false otherwise
+     * @return generated character
+     */
+    public char getCharacterFor(VirtualKey key,
+                                boolean numlock,
+                                boolean shift,
+                                boolean altgr,
+                                boolean inhibit)
+    {
+        List<KeyStroke> candidates = keyStrokes.get(key);
+
+        if (candidates == null) {
+            return Character.UNASSIGNED;
+        }
+
+        // try to find the key stroke
+        for (KeyStroke keyStroke : candidates) {
+            if ((numlock == keyStroke.getNumlock()) && (!keyStroke.getLocalstate()) &&
+                (shift == keyStroke.getShift()) && (altgr == keyStroke.getAltgr()) &&
+                (inhibit == keyStroke.getInhibit()))
+            {
+                return keyStroke.getCharacter();
+            }
+        }
+
+        // try to find the key stroke with a local state ignoring the other keys
+        for (KeyStroke keyStroke : candidates) {
+            if ((numlock == keyStroke.getNumlock()) && (keyStroke.getLocalstate()) &&
+                (inhibit == keyStroke.getInhibit()))
+            {
+                return keyStroke.getCharacter();
+            }
+        }
+
+        return Character.UNASSIGNED;
+    }
+
+    /**
+     * 
+     * <p>
+     * Initializes the keyboard map.
+     * </p>
+     * 
+     * @throws IllegalArgumentException thrown if there is a problem loading the keyboard map
+     */
+    void init() throws IllegalArgumentException {
+        Console.traceln(Level.FINE, "initializing keymap for locale " + locale);
+
+        List<String[]> deadKeySequences = new ArrayList<String[]>();
+        List<String[]> keyStrokes = new ArrayList<String[]>();
+        readStream(getStream(fileName), deadKeySequences, keyStrokes);
+
+        Console.traceln(Level.FINER, "read " + keyStrokes.size() + " key strokes and " +
+            deadKeySequences.size() + " dead key sequences");
+
+        VirtualKeySynonyms virtualKeySynonyms = determineVirtualKeySynonyms(keyStrokes);
+        processKeyStrokes(keyStrokes, virtualKeySynonyms);
+        // TODO still required? processDeadKeySequences(deadKeySequences);
+    }
+
+    /**
+     * <p>
+     * Returns a {@link InputStream} for a given filename.
+     * </p>
+     * 
+     * @param name
+     *            the filename
+     * @return the {@link InputStream}
+     */
+    private InputStream getStream(String name) {
+        Console.traceln(Level.FINER, "reading keymap for locale " + locale +
+            " from resource keymaps/" + name);
+
+        InputStream stream =
+            this.getClass().getClassLoader().getResourceAsStream("keymaps/" + name);
+
+        if (stream == null) {
+            throw new IllegalArgumentException("no keyboard map available for locale " + locale);
+        }
+
+        return stream;
+    }
+
+    /**
+     * <p>
+     * Reads a keyboard map from a input stream.
+     * </p>
+     * 
+     * @param stream
+     *            input stream with the keyboard map
+     * @param deadKeySequences
+     *            the dead key sequences
+     * @param keyStrokes
+     *            the keystrokes
+     * @throws IllegalArgumentException
+     *             thrown if there is an error reading the keyboard map
+     */
+    private void readStream(InputStream stream,
+                            List<String[]> deadKeySequences,
+                            List<String[]> keyStrokes) throws IllegalArgumentException
+    {
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
+        }
+        catch (UnsupportedEncodingException e) {
+            Console.traceln(Level.WARNING, "no keyboard map available for locale " + locale);
+            throw new IllegalArgumentException("provided stream can not be read due to invalid encoding",
+                                               e);
+        }
+
+        try {
+            String line;
+            while ((line = in.readLine()) != null) {
+                if (!"".equals(line)) {
+                    processLine(line, deadKeySequences, keyStrokes);
+                }
+            }
+        }
+        catch (IOException e) {
+            Console.traceln(Level.WARNING, "no keyboard map available for locale " + locale);
+            throw new IllegalArgumentException("no keyboard map available for locale " + locale, e);
+        }
+        finally {
+            try {
+                in.close();
+            }
+            catch (IOException e) {
+                Console.traceln(Level.WARNING,
+                                "could not close input stream for reading keyboard map");
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Processes a line of a keyboard map file.
+     * </p>
+     * 
+     * @param line
+     *            the line in the keyboard map file
+     * @param deadKeySequences
+     *            the dead key sequences
+     * @param keyStrokes
+     *            the keystrokes
+     */
+    private void processLine(String line, List<String[]> deadKeySequences, List<String[]> keyStrokes)
+    {
+        String[] values = line.split(" ");
+
+        if (values.length <= 0) {
+            return;
+        }
+
+        // ignore comments
+        if (values[0].startsWith("#")) {
+            return;
+        }
+
+        if ("map".equals(values[0])) {
+            // this is the map id. Ignore it.
+        }
+        else if ("include".equals(values[0])) {
+            // process all includes
+            for (int i = 1; i < values.length; i++) {
+                if (!values[i].startsWith("#")) {
+                    readStream(getStream(values[i]), deadKeySequences, keyStrokes);
+                }
+                else {
+                    break;
+                }
+            }
+        }
+        else if ("sequence".equals(values[0])) {
+            deadKeySequences.add(values);
+        }
+        else {
+            boolean alreadyAdded = false;
+
+            // check, if there is a replacement
+            for (int i = 0; i < keyStrokes.size(); i++) {
+                if (keyStrokes.get(i)[0].equals(values[0])) {
+                    Console.traceln(Level.FINEST, "replacing key stroke " + values[0] +
+                                    " with former keyid " + keyStrokes.get(i)[1] +
+                                    " with new key id " + values[1]);
+                    keyStrokes.set(i, values);
+                    alreadyAdded = true;
+                    break;
+                }
+            }
+
+            if (!alreadyAdded) {
+                keyStrokes.add(values);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Determines synonyms for virtual keys for the keyboard map.
+     * </p>
+     * 
+     * @param keyStrokes
+     *            the keystrokes
+     * @return the synonyms
+     */
+    private VirtualKeySynonyms determineVirtualKeySynonyms(List<String[]> keyStrokes) {
+        Console.traceln(Level.FINER, "determining virtual keys and synonyms for the keymap");
+
+        VirtualKeySynonyms virtualKeySynonyms = new VirtualKeySynonyms();
+
+        // for debugging purposes, determine which key strokes are not matched to virtual keys.
+        List<String[]> unmatchedKeyStrokes = new ArrayList<String[]>();
+
+        for (String[] keyStroke : keyStrokes) {
+            String keyStrokeName = keyStroke[0];
+            int keyId = getKeyId(keyStroke[1]);
+            // System.err.println(keyStrokeName + "  0x" + Integer.toHexString(keyId));
+
+            VirtualKey virtualKey = determineVirtualKey(keyStrokeName);
+
+            if (virtualKey != null) {
+                virtualKeySynonyms.add(keyId, virtualKey);
+            }
+            else {
+                unmatchedKeyStrokes.add(keyStroke);
+            }
+        }
+
+        for (String[] unmatchedKeyStroke : unmatchedKeyStrokes) {
+            if (!virtualKeySynonyms.containsKey(getKeyId(unmatchedKeyStroke[1]))) {
+                Console.traceln(Level.FINEST, "no virtual key mapped to key stroke " +
+                                unmatchedKeyStroke[0] + "(" + unmatchedKeyStroke[1] +
+                                ") of keyboard map for locale " + locale);
+            }
+        }
+
+        return virtualKeySynonyms;
+    }
+
+    /**
+     * <p>
+     * Converts a hexadecimal Id that is contained in a string into the integer Id of the key.
+     * </p>
+     * 
+     * @param keyIdString
+     *            the Id string
+     * @return the Id as integer
+     */
+    private int getKeyId(String keyIdString) {
+        if (keyIdString.startsWith("0x")) {
+            keyIdString = keyIdString.substring(2);
+        }
+
+        return Integer.parseInt(keyIdString, 16);
+    }
+
+    /**
+     * <p>
+     * Processes a list of key strokes.
+     * </p>
+     * 
+     * @param keyStrokes
+     *            the key strokes
+     * @param virtualKeySynonyms
+     *            synonyms of the involved virtual keys
+     */
+    private void processKeyStrokes(List<String[]> keyStrokes, VirtualKeySynonyms virtualKeySynonyms)
+    {
+        for (String[] keyStroke : keyStrokes) {
+            handleKeyStroke(keyStroke, virtualKeySynonyms);
+        }
+
+        addKeyStrokesIndependentOfNumLock();
+    }
+
+    /**
+     * <p>
+     * Handles a key stroke.
+     * </p>
+     * 
+     * @param values
+     *            contains the name, string Id, and modifiers of the key stroke
+     * @param virtualKeySynonyms
+     *            synonyms of the involved virtual keys
+     */
+    private void handleKeyStroke(String[] values, VirtualKeySynonyms virtualKeySynonyms) {
+        String keyStrokeName = values[0];
+        String keyIdString = values[1];
+        if (keyIdString.startsWith("0x")) {
+            keyIdString = keyIdString.substring(2);
+        }
+
+        int keyId = Integer.parseInt(keyIdString, 16);
+
+        // parse the conditions
+        boolean numlock = false;
+        boolean localstate = false;
+        boolean shift = false;
+        boolean altgr = false;
+        boolean addupper = false;
+        boolean inhibit = false;
+
+        for (int i = 2; i < values.length; i++) {
+            if (!values[i].startsWith("#")) {
+                if ("numlock".equals(values[i])) {
+                    numlock = true;
+                }
+                else if ("localstate".equals(values[i])) {
+                    localstate = true;
+                }
+                else if ("shift".equals(values[i])) {
+                    shift = true;
+                }
+                else if ("altgr".equals(values[i])) {
+                    altgr = true;
+                }
+                else if ("addupper".equals(values[i])) {
+                    addupper = true;
+                }
+                else if ("inhibit".equals(values[i])) {
+                    inhibit = true;
+                }
+                else {
+                    Console.traceln(Level.SEVERE, "unknown condition " + values[i] +
+                                    " specified for key stroke " + keyStrokeName +
+                                    " through keyboard map for locale " + locale);
+                    throw new IllegalArgumentException
+                        ("no keyboard map available for locale " + locale);
+                }
+            }
+            else {
+                break;
+            }
+        }
+
+        addAllRepresentedKeyStrokes(keyStrokeName, keyId, numlock, localstate, shift, altgr,
+                                    addupper, inhibit, virtualKeySynonyms);
+    }
+
+    /**
+     * <p>
+     * Adds a key stroke and all synonyms to the keyboard map.
+     * </p>
+     * 
+     * @param keyStrokeName
+     *            name of the key stroke
+     * @param keyId
+     *            id of the key stroke
+     * @param numlock
+     *            true, if numlock is pressed; false otherwise
+     * @param localstate
+     *            true, if localstate is pressed; false otherwise
+     * @param shift
+     *            true, if shift is pressed; false otherwise
+     * @param altgr
+     *            true, if altgr is pressed; false otherwise
+     * @param addupper
+     *            true, if addupper is pressed; false otherwise
+     * @param inhibit
+     *            true, if inhibit is pressed; false otherwise
+     * @param virtualKeySynonyms
+     *            synonyms of the involved virtual keys
+     */
+    private void addAllRepresentedKeyStrokes(String keyStrokeName,
+                                             int keyId,
+                                             boolean numlock,
+                                             boolean localstate,
+                                             boolean shift,
+                                             boolean altgr,
+                                             boolean addupper,
+                                             boolean inhibit,
+                                             VirtualKeySynonyms virtualKeySynonyms)
+    {
+        VirtualKey[] virtualKeys = virtualKeySynonyms.getVirtualKeySynonyms(keyId);
+
+        if (virtualKeys == null) {
+            Console.traceln(Level.SEVERE, "no virtual key mapped to key stroke " + keyStrokeName +
+                " of keyboard map for locale " + locale);
+            return;
+        }
+
+        for (VirtualKey virtualKey : virtualKeys) {
+            if (addupper) {
+                char c = determineCharacter(keyStrokeName, true);
+                addKeyStroke(keyStrokeName, virtualKey, numlock, localstate, true, altgr, inhibit,
+                             c);
+
+                c = determineCharacter(keyStrokeName, false);
+                addKeyStroke(keyStrokeName, virtualKey, numlock, localstate, false, altgr, inhibit,
+                             c);
+            }
+            else {
+                char c = determineCharacter(keyStrokeName, false);
+                addKeyStroke(keyStrokeName, virtualKey, numlock, localstate, shift, altgr, inhibit,
+                             c);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Adds a key stroke and to the keyboard map.
+     * </p>
+     * 
+     * @param keyStrokeName
+     *            name of the key stroke
+     * @param virtualKey
+     *            the associated virtual key
+     * @param numlock
+     *            true, if numlock is pressed; false otherwise
+     * @param localstate
+     *            true, if localstate is pressed; false otherwise
+     * @param shift
+     *            true, if shift is pressed; false otherwise
+     * @param altgr
+     *            true, if altgr is pressed; false otherwise
+     * @param addupper
+     *            true, if addupper is pressed; false otherwise
+     * @param inhibit
+     *            true, if inhibit is pressed; false otherwise
+     * @param character
+     *            the resulting character
+     */
+    private void addKeyStroke(String keyStrokeName,
+                              VirtualKey virtualKey,
+                              boolean numlock,
+                              boolean localstate,
+                              boolean shift,
+                              boolean altgr,
+                              boolean inhibit,
+                              char character)
+    {
+        KeyStroke keyStroke =
+            new KeyStroke(keyStrokeName, virtualKey, numlock, localstate, shift, altgr, inhibit,
+                          character);
+
+        List<KeyStroke> keyStrokeList = keyStrokes.get(keyStroke.getVirtualKey());
+
+        if (keyStrokeList == null) {
+            keyStrokeList = new ArrayList<KeyStroke>();
+            keyStrokes.put(keyStroke.getVirtualKey(), keyStrokeList);
+        }
+
+        keyStrokeList.add(keyStroke);
+    }
+
+    /**
+     * <p>
+     * Adds key strokes independent of numlock.
+     * </p>
+     */
+    private void addKeyStrokesIndependentOfNumLock() {
+        for (Map.Entry<VirtualKey, List<KeyStroke>> entry : keyStrokes.entrySet()) {
+            List<KeyStroke> keyStrokesToAdd = new ArrayList<KeyStroke>();
+            for (KeyStroke keyStroke : entry.getValue()) {
+                if (!keyStroke.getNumlock()) {
+                    boolean foundPositiveNumlockVariant = false;
+                    for (KeyStroke candidate : entry.getValue()) {
+                        if ((candidate.getShift() == keyStroke.getShift()) &&
+                            (candidate.getAltgr() == keyStroke.getAltgr()) &&
+                            (candidate.getLocalstate() == keyStroke.getLocalstate()) &&
+                            (candidate.getInhibit() == keyStroke.getInhibit()) &&
+                            (candidate.getNumlock()))
+                        {
+                            foundPositiveNumlockVariant = true;
+                            break;
+                        }
+                    }
+
+                    if (!foundPositiveNumlockVariant) {
+                        keyStrokesToAdd.add(keyStroke);
+                    }
+                }
+            }
+
+            for (KeyStroke keyStroke : keyStrokesToAdd) {
+                addKeyStroke(keyStroke.getKeyStrokeName(), keyStroke.getVirtualKey(), true,
+                             keyStroke.getLocalstate(), keyStroke.getShift(), keyStroke.getAltgr(),
+                             keyStroke.getInhibit(), keyStroke.getCharacter());
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Determines a {@link VirtualKey} depending on a key stroke name
+     * </p>
+     * 
+     * @param keyStrokeName
+     *            name of the key stroke
+     * @return related virtual key
+     */
+    private VirtualKey determineVirtualKey(String keyStrokeName) {
+        if ("Shift_R".equals(keyStrokeName)) {
+            return VirtualKey.SHIFT;
+        }
+        else if ("Shift_L".equals(keyStrokeName)) {
+            return VirtualKey.SHIFT;
+        }
+        else if ("Alt_R".equals(keyStrokeName)) {
+            return VirtualKey.ALT_GRAPH;
+        }
+        else if ("Mode_switch".equals(keyStrokeName)) {
+            return VirtualKey.MODECHANGE;
+        }
+        else if ("ISO_Level3_Shift".equals(keyStrokeName)) {
+            return VirtualKey.SHIFT;
+        }
+        else if ("Alt_L".equals(keyStrokeName)) {
+            return VirtualKey.ALT;
+        }
+        else if ("Control_R".equals(keyStrokeName)) {
+            return VirtualKey.CONTROL;
+        }
+        else if ("Control_L".equals(keyStrokeName)) {
+            return VirtualKey.CONTROL;
+        }
+        else if ("Menu".equals(keyStrokeName)) {
+            return VirtualKey.WINDOWS;
+        }
+        else if ("1".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_1;
+        }
+        else if ("2".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_2;
+        }
+        else if ("3".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_3;
+        }
+        else if ("4".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_4;
+        }
+        else if ("5".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_5;
+        }
+        else if ("6".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_6;
+        }
+        else if ("7".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_7;
+        }
+        else if ("8".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_8;
+        }
+        else if ("9".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_9;
+        }
+        else if ("0".equals(keyStrokeName)) {
+            return VirtualKey.DIGIT_0;
+        }
+        else if ("BackSpace".equals(keyStrokeName)) {
+            return VirtualKey.BACK_SPACE;
+        }
+        else if ("Tab".equals(keyStrokeName)) {
+            return VirtualKey.TAB;
+        }
+        else if ("q".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_Q;
+        }
+        else if ("w".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_W;
+        }
+        else if ("e".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_E;
+        }
+        else if ("r".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_R;
+        }
+        else if ("t".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_T;
+        }
+        else if ("y".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_Y;
+        }
+        else if ("u".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_U;
+        }
+        else if ("i".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_I;
+        }
+        else if ("o".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_O;
+        }
+        else if ("p".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_P;
+        }
+        else if ("a".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_A;
+        }
+        else if ("s".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_S;
+        }
+        else if ("d".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_D;
+        }
+        else if ("f".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_F;
+        }
+        else if ("g".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_G;
+        }
+        else if ("h".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_H;
+        }
+        else if ("j".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_J;
+        }
+        else if ("k".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_K;
+        }
+        else if ("l".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_L;
+        }
+        else if ("Return".equals(keyStrokeName)) {
+            return VirtualKey.ENTER;
+        }
+        else if ("z".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_Z;
+        }
+        else if ("x".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_X;
+        }
+        else if ("c".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_C;
+        }
+        else if ("v".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_V;
+        }
+        else if ("b".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_B;
+        }
+        else if ("n".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_N;
+        }
+        else if ("m".equals(keyStrokeName)) {
+            return VirtualKey.LETTER_M;
+        }
+        else if ("space".equals(keyStrokeName)) {
+            return VirtualKey.SPACE;
+        }
+        else if ("less".equals(keyStrokeName)) {
+            return VirtualKey.LESS;
+        }
+        else if ("greater".equals(keyStrokeName)) {
+            return VirtualKey.GREATER;
+        }
+        else if ("Escape".equals(keyStrokeName)) {
+            return VirtualKey.ESCAPE;
+        }
+        else if ("F1".equals(keyStrokeName)) {
+            return VirtualKey.F1;
+        }
+        else if ("F2".equals(keyStrokeName)) {
+            return VirtualKey.F2;
+        }
+        else if ("F3".equals(keyStrokeName)) {
+            return VirtualKey.F3;
+        }
+        else if ("F4".equals(keyStrokeName)) {
+            return VirtualKey.F4;
+        }
+        else if ("F5".equals(keyStrokeName)) {
+            return VirtualKey.F5;
+        }
+        else if ("F6".equals(keyStrokeName)) {
+            return VirtualKey.F6;
+        }
+        else if ("F7".equals(keyStrokeName)) {
+            return VirtualKey.F7;
+        }
+        else if ("F8".equals(keyStrokeName)) {
+            return VirtualKey.F8;
+        }
+        else if ("F9".equals(keyStrokeName)) {
+            return VirtualKey.F9;
+        }
+        else if ("F10".equals(keyStrokeName)) {
+            return VirtualKey.F10;
+        }
+        else if ("F11".equals(keyStrokeName)) {
+            return VirtualKey.F11;
+        }
+        else if ("F12".equals(keyStrokeName)) {
+            return VirtualKey.F12;
+        }
+        else if ("F13".equals(keyStrokeName)) {
+            return VirtualKey.F13;
+        }
+        else if ("F14".equals(keyStrokeName)) {
+            return VirtualKey.F14;
+        }
+        else if ("F15".equals(keyStrokeName)) {
+            return VirtualKey.F15;
+        }
+        else if ("F16".equals(keyStrokeName)) {
+            return VirtualKey.F16;
+        }
+        else if ("F17".equals(keyStrokeName)) {
+            return VirtualKey.F17;
+        }
+        else if ("F18".equals(keyStrokeName)) {
+            return VirtualKey.F18;
+        }
+        else if ("F19".equals(keyStrokeName)) {
+            return VirtualKey.F19;
+        }
+        else if ("F20".equals(keyStrokeName)) {
+            return VirtualKey.F20;
+        }
+        else if ("F21".equals(keyStrokeName)) {
+            return VirtualKey.F21;
+        }
+        else if ("F22".equals(keyStrokeName)) {
+            return VirtualKey.F22;
+        }
+        else if ("F23".equals(keyStrokeName)) {
+            return VirtualKey.F23;
+        }
+        else if ("F24".equals(keyStrokeName)) {
+            return VirtualKey.F24;
+        }
+        else if ("Print".equals(keyStrokeName)) {
+            return VirtualKey.PRINTSCREEN;
+        }
+        else if ("Scroll_Lock".equals(keyStrokeName)) {
+            return VirtualKey.SCROLL_LOCK;
+        }
+        else if ("Insert".equals(keyStrokeName)) {
+            return VirtualKey.INSERT;
+        }
+        else if ("Delete".equals(keyStrokeName)) {
+            return VirtualKey.DELETE;
+        }
+        else if ("Home".equals(keyStrokeName)) {
+            return VirtualKey.HOME;
+        }
+        else if ("End".equals(keyStrokeName)) {
+            return VirtualKey.END;
+        }
+        else if ("Page_Up".equals(keyStrokeName)) {
+            return VirtualKey.PAGE_UP;
+        }
+        else if ("Page_Down".equals(keyStrokeName)) {
+            return VirtualKey.PAGE_DOWN;
+        }
+        else if ("Left".equals(keyStrokeName)) {
+            return VirtualKey.LEFT;
+        }
+        else if ("Up".equals(keyStrokeName)) {
+            return VirtualKey.UP;
+        }
+        else if ("Down".equals(keyStrokeName)) {
+            return VirtualKey.DOWN;
+        }
+        else if ("Right".equals(keyStrokeName)) {
+            return VirtualKey.RIGHT;
+        }
+        else if ("Num_Lock".equals(keyStrokeName)) {
+            return VirtualKey.NUM_LOCK;
+        }
+        else if ("KP_Divide".equals(keyStrokeName)) {
+            return VirtualKey.SLASH;
+        }
+        else if ("KP_Multiply".equals(keyStrokeName)) {
+            return VirtualKey.ASTERISK;
+        }
+        else if ("KP_Subtract".equals(keyStrokeName)) {
+            return VirtualKey.MINUS;
+        }
+        else if ("KP_Add".equals(keyStrokeName)) {
+            return VirtualKey.PLUS;
+        }
+        else if ("KP_Enter".equals(keyStrokeName)) {
+            return VirtualKey.ENTER;
+        }
+        else if ("KP_Decimal".equals(keyStrokeName)) {
+            return VirtualKey.DECIMAL;
+        }
+        else if ("KP_Separator".equals(keyStrokeName)) {
+            return VirtualKey.SEPARATOR;
+        }
+        else if ("KP_Delete".equals(keyStrokeName)) {
+            return VirtualKey.DELETE;
+        }
+        else if ("KP_0".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_0;
+        }
+        else if ("KP_Insert".equals(keyStrokeName)) {
+            return VirtualKey.INSERT;
+        }
+        else if ("KP_1".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_1;
+        }
+        else if ("KP_End".equals(keyStrokeName)) {
+            return VirtualKey.END;
+        }
+        else if ("KP_2".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_2;
+        }
+        else if ("KP_Down".equals(keyStrokeName)) {
+            return VirtualKey.KP_DOWN;
+        }
+        else if ("KP_3".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_3;
+        }
+        else if ("KP_Next".equals(keyStrokeName)) {
+            return VirtualKey.PAGE_DOWN;
+        }
+        else if ("KP_4".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_4;
+        }
+        else if ("KP_Left".equals(keyStrokeName)) {
+            return VirtualKey.KP_LEFT;
+        }
+        else if ("KP_5".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_5;
+        }
+        else if ("KP_Begin".equals(keyStrokeName)) {
+            return VirtualKey.BEGIN;
+        }
+        else if ("KP_6".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_6;
+        }
+        else if ("KP_Right".equals(keyStrokeName)) {
+            return VirtualKey.KP_RIGHT;
+        }
+        else if ("KP_7".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_7;
+        }
+        else if ("KP_Home".equals(keyStrokeName)) {
+            return VirtualKey.HOME;
+        }
+        else if ("KP_8".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_8;
+        }
+        else if ("KP_Up".equals(keyStrokeName)) {
+            return VirtualKey.KP_UP;
+        }
+        else if ("KP_9".equals(keyStrokeName)) {
+            return VirtualKey.NUMPAD_9;
+        }
+        else if ("KP_Prior".equals(keyStrokeName)) {
+            return VirtualKey.PAGE_UP;
+        }
+        else if ("Caps_Lock".equals(keyStrokeName)) {
+            return VirtualKey.CAPS_LOCK;
+        }
+        else if ("exclam".equals(keyStrokeName)) {
+            return VirtualKey.EXCLAMATION_MARK;
+        }
+        else if ("exclamdown".equals(keyStrokeName)) {
+            return VirtualKey.INVERTED_EXCLAMATION_MARK;
+        }
+        else if ("quotedbl".equals(keyStrokeName)) {
+            return VirtualKey.QUOTEDBL;
+        }
+        else if ("slash".equals(keyStrokeName)) {
+            return VirtualKey.SLASH;
+        }
+        else if ("backslash".equals(keyStrokeName)) {
+            return VirtualKey.BACK_SLASH;
+        }
+        else if ("dead_acute".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_ACUTE;
+        }
+        else if ("dead_diaresis".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_DIAERESIS;
+        }
+        else if ("dead_abovering".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_ABOVERING;
+        }
+        else if ("plus".equals(keyStrokeName)) {
+            return VirtualKey.PLUS;
+        }
+        else if ("asterisk".equals(keyStrokeName)) {
+            return VirtualKey.ASTERISK;
+        }
+        else if ("dead_tilde".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_TILDE;
+        }
+        else if ("dead_doubleacute".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_DOUBLEACUTE;
+        }
+        else if ("dead_caron".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_CARON;
+        }
+        else if ("dead_circumflex".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_CIRCUMFLEX;
+        }
+        else if ("comma".equals(keyStrokeName)) {
+            return VirtualKey.COMMA;
+        }
+        else if ("semicolon".equals(keyStrokeName)) {
+            return VirtualKey.SEMICOLON;
+        }
+        else if ("multiply".equals(keyStrokeName)) {
+            return VirtualKey.MULTIPLY;
+        }
+        else if ("period".equals(keyStrokeName)) {
+            return VirtualKey.PERIOD;
+        }
+        else if ("colon".equals(keyStrokeName)) {
+            return VirtualKey.COLON;
+        }
+        else if ("dead_breve".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_BREVE;
+        }
+        else if ("division".equals(keyStrokeName)) {
+            return VirtualKey.DIVIDE;
+        }
+        else if ("minus".equals(keyStrokeName)) {
+            return VirtualKey.MINUS;
+        }
+        else if ("underscore".equals(keyStrokeName)) {
+            return VirtualKey.UNDERSCORE;
+        }
+        else if ("dead_abovedot".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_ABOVEDOT;
+        }
+        else if ("bracketleft".equals(keyStrokeName)) {
+            return VirtualKey.OPEN_BRACKET;
+        }
+        else if ("bracketright".equals(keyStrokeName)) {
+            return VirtualKey.CLOSE_BRACKET;
+        }
+        else if ("grave".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_GRAVE;
+        }
+        else if ("equal".equals(keyStrokeName)) {
+            return VirtualKey.EQUALS;
+        }
+        else if ("dead_macron".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_MACRON;
+        }
+        else if ("dead_ogonek".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_OGONEK;
+        }
+        else if ("dead_cedilla".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_CEDILLA;
+        }
+        else if ("ampersand".equals(keyStrokeName)) {
+            return VirtualKey.AMPERSAND;
+        }
+        else if ("parenleft".equals(keyStrokeName)) {
+            return VirtualKey.LEFT_PARENTHESIS;
+        }
+        else if ("parenright".equals(keyStrokeName)) {
+            return VirtualKey.RIGHT_PARENTHESIS;
+        }
+        else if ("braceleft".equals(keyStrokeName)) {
+            return VirtualKey.BRACELEFT;
+        }
+        else if ("braceright".equals(keyStrokeName)) {
+            return VirtualKey.BRACERIGHT;
+        }
+        else if ("at".equals(keyStrokeName)) {
+            return VirtualKey.AT;
+        }
+        else if ("dollar".equals(keyStrokeName)) {
+            return VirtualKey.DOLLAR;
+        }
+        else if ("EuroSign".equals(keyStrokeName)) {
+            return VirtualKey.EURO_SIGN;
+        }
+        else if ("Begin".equals(keyStrokeName)) {
+            return VirtualKey.BEGIN;
+        }
+        else if ("numbersign".equals(keyStrokeName)) {
+            return VirtualKey.NUMBER_SIGN;
+        }
+        else if ("asciicircum".equals(keyStrokeName)) {
+            return VirtualKey.CIRCUMFLEX;
+        }
+        else if ("Kanji".equals(keyStrokeName)) {
+            return VirtualKey.KANJI;
+        }
+        else if ("Katakana".equals(keyStrokeName)) {
+            return VirtualKey.KATAKANA;
+        }
+        else if ("Hiragana_Katakana".equals(keyStrokeName)) {
+            return VirtualKey.HIRAGANA;
+        }
+        else if ("Muhenkan".equals(keyStrokeName)) {
+            // I found this in the KeyEvent description
+            return VirtualKey.NONCONVERT;
+        }
+        else if ("kan".equals(keyStrokeName)) {
+            // I found this in the KeyEvent description
+            return VirtualKey.NONCONVERT;
+        }
+        else if ("Henkan_Mode".equals(keyStrokeName)) {
+            // I found this in the key event description
+            return VirtualKey.CONVERT;
+        }
+        else if ("voicedsound".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_VOICED_SOUND;
+        }
+        else if ("semivoicedsound".equals(keyStrokeName)) {
+            return VirtualKey.DEAD_SEMIVOICED_SOUND;
+        }
+        else if ("Menu".equals(keyStrokeName)) {
+            return VirtualKey.CONTEXT_MENU;
+        }
+        else {
+            Console.traceln(Level.FINEST, "unknown virtual key for key stroke " + keyStrokeName +
+                " specified through " + "keyboard map for locale " + locale);
+
+            return null;
+        }
+
+        // for the following virtual keys no key stroke names are provided in the key maps
+        /*
+         * CANCEL(KeyEvent.VK_CANCEL), CLEAR(KeyEvent.VK_CLEAR), PAUSE(KeyEvent.VK_PAUSE),
+         * HELP(KeyEvent.VK_HELP), META(KeyEvent.VK_META),
+         * 
+         * BACK_QUOTE(KeyEvent.VK_BACK_QUOTE), QUOTE(KeyEvent.VK_QUOTE),
+         * 
+         * DEAD_IOTA(KeyEvent.VK_DEAD_IOTA),
+         * 
+         * FINAL(KeyEvent.VK_FINAL), CONVERT(KeyEvent.VK_CONVERT),
+         * NONCONVERT(KeyEvent.VK_NONCONVERT), ACCEPT(KeyEvent.VK_ACCEPT), KANA(KeyEvent.VK_KANA),
+         * ALPHANUMERIC(KeyEvent.VK_ALPHANUMERIC), FULL_WIDTH(KeyEvent.VK_FULL_WIDTH),
+         * HALF_WIDTH(KeyEvent.VK_HALF_WIDTH), ROMAN_CHARACTERS(KeyEvent.VK_ROMAN_CHARACTERS),
+         * ALL_CANDIDATES(KeyEvent.VK_ALL_CANDIDATES),
+         * PREVIOUS_CANDIDATE(KeyEvent.VK_PREVIOUS_CANDIDATE), CODE_INPUT(KeyEvent.VK_CODE_INPUT),
+         * JAPANESE_KATAKANA(KeyEvent.VK_JAPANESE_KATAKANA),
+         * JAPANESE_HIRAGANA(KeyEvent.VK_JAPANESE_HIRAGANA),
+         * JAPANESE_ROMAN(KeyEvent.VK_JAPANESE_ROMAN), KANA_LOCK(KeyEvent.VK_KANA_LOCK),
+         * INPUT_METHOD_ON_OFF(KeyEvent.VK_INPUT_METHOD_ON_OFF),
+         * 
+         * CUT(KeyEvent.VK_CUT), COPY(KeyEvent.VK_COPY), PASTE(KeyEvent.VK_PASTE),
+         * UNDO(KeyEvent.VK_UNDO), AGAIN(KeyEvent.VK_AGAIN), FIND(KeyEvent.VK_FIND),
+         * PROPS(KeyEvent.VK_PROPS), STOP(KeyEvent.VK_STOP), COMPOSE(KeyEvent.VK_COMPOSE),
+         */
+    }
+
+    /**
+     * <p>
+     * Determines the character that is generated by a key stroke.
+     * </p>
+     *
+     * @param keyStrokeName name of the key stroke
+     * @param getUpper defines whether the upper case version of the key stroke is returned or not
+     * @return the character
+     */
+    private char determineCharacter(String keyStrokeName, boolean getUpper) {
+        if ("Shift_R".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Shift_L".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Alt_R".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Mode_switch".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("ISO_Level3_Shift".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Alt_L".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Control_R".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Control_L".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Menu".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("1".equals(keyStrokeName)) {
+            return '1';
+        }
+        else if ("2".equals(keyStrokeName)) {
+            return '2';
+        }
+        else if ("3".equals(keyStrokeName)) {
+            return '3';
+        }
+        else if ("4".equals(keyStrokeName)) {
+            return '4';
+        }
+        else if ("5".equals(keyStrokeName)) {
+            return '5';
+        }
+        else if ("6".equals(keyStrokeName)) {
+            return '6';
+        }
+        else if ("7".equals(keyStrokeName)) {
+            return '7';
+        }
+        else if ("8".equals(keyStrokeName)) {
+            return '8';
+        }
+        else if ("9".equals(keyStrokeName)) {
+            return '9';
+        }
+        else if ("0".equals(keyStrokeName)) {
+            return '0';
+        }
+        else if ("BackSpace".equals(keyStrokeName)) {
+            return '\b';
+        }
+        else if ("Tab".equals(keyStrokeName)) {
+            return '\t';
+        }
+        else if ("ISO_Left_Tab".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("q".equals(keyStrokeName)) {
+            return getUpper ? 'Q' : 'q';
+        }
+        else if ("w".equals(keyStrokeName)) {
+            return getUpper ? 'W' : 'w';
+        }
+        else if ("e".equals(keyStrokeName)) {
+            return getUpper ? 'E' : 'e';
+        }
+        else if ("r".equals(keyStrokeName)) {
+            return getUpper ? 'R' : 'r';
+        }
+        else if ("t".equals(keyStrokeName)) {
+            return getUpper ? 'T' : 't';
+        }
+        else if ("y".equals(keyStrokeName)) {
+            return getUpper ? 'Y' : 'y';
+        }
+        else if ("u".equals(keyStrokeName)) {
+            return getUpper ? 'U' : 'u';
+        }
+        else if ("i".equals(keyStrokeName)) {
+            return getUpper ? 'I' : 'i';
+        }
+        else if ("o".equals(keyStrokeName)) {
+            return getUpper ? 'O' : 'o';
+        }
+        else if ("p".equals(keyStrokeName)) {
+            return getUpper ? 'P' : 'p';
+        }
+        else if ("a".equals(keyStrokeName)) {
+            return getUpper ? 'A' : 'a';
+        }
+        else if ("s".equals(keyStrokeName)) {
+            return getUpper ? 'S' : 's';
+        }
+        else if ("d".equals(keyStrokeName)) {
+            return getUpper ? 'D' : 'd';
+        }
+        else if ("f".equals(keyStrokeName)) {
+            return getUpper ? 'F' : 'f';
+        }
+        else if ("g".equals(keyStrokeName)) {
+            return getUpper ? 'G' : 'g';
+        }
+        else if ("h".equals(keyStrokeName)) {
+            return getUpper ? 'H' : 'h';
+        }
+        else if ("j".equals(keyStrokeName)) {
+            return getUpper ? 'J' : 'j';
+        }
+        else if ("k".equals(keyStrokeName)) {
+            return getUpper ? 'K' : 'k';
+        }
+        else if ("l".equals(keyStrokeName)) {
+            return getUpper ? 'L' : 'l';
+        }
+        else if ("Return".equals(keyStrokeName)) {
+            return '\n';
+        }
+        else if ("z".equals(keyStrokeName)) {
+            return getUpper ? 'Z' : 'z';
+        }
+        else if ("x".equals(keyStrokeName)) {
+            return getUpper ? 'X' : 'x';
+        }
+        else if ("c".equals(keyStrokeName)) {
+            return getUpper ? 'C' : 'c';
+        }
+        else if ("v".equals(keyStrokeName)) {
+            return getUpper ? 'V' : 'v';
+        }
+        else if ("b".equals(keyStrokeName)) {
+            return getUpper ? 'B' : 'b';
+        }
+        else if ("n".equals(keyStrokeName)) {
+            return getUpper ? 'N' : 'n';
+        }
+        else if ("m".equals(keyStrokeName)) {
+            return getUpper ? 'M' : 'm';
+        }
+        else if ("space".equals(keyStrokeName)) {
+            return ' ';
+        }
+        else if ("less".equals(keyStrokeName)) {
+            return '<';
+        }
+        else if ("greater".equals(keyStrokeName)) {
+            return '>';
+        }
+        else if ("bar".equals(keyStrokeName)) {
+            return '|';
+        }
+        else if ("brokenbar".equals(keyStrokeName)) {
+            return '¦';
+        }
+        else if ("Escape".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F1".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F2".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F3".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F4".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F5".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F6".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F7".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F8".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F9".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F10".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F11".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("SunF36".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F12".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("SunF37".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Print".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Sys_Req".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Execute".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F22".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Scroll_Lock".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F23".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Insert".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Delete".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Home".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("End".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Page_Up".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Page_Down".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Left".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Up".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Down".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Right".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Num_Lock".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_Divide".equals(keyStrokeName)) {
+            return '/';
+        }
+        else if ("KP_Multiply".equals(keyStrokeName)) {
+            return '*';
+        }
+        else if ("KP_Subtract".equals(keyStrokeName)) {
+            return '-';
+        }
+        else if ("KP_Add".equals(keyStrokeName)) {
+            return '+';
+        }
+        else if ("KP_Enter".equals(keyStrokeName)) {
+            return '\n';
+        }
+        else if ("KP_Decimal".equals(keyStrokeName)) {
+            return ',';
+        }
+        else if ("KP_Separator".equals(keyStrokeName)) {
+            return ',';
+        }
+        else if ("KP_Delete".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_0".equals(keyStrokeName)) {
+            return '0';
+        }
+        else if ("KP_Insert".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_1".equals(keyStrokeName)) {
+            return '1';
+        }
+        else if ("KP_End".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_2".equals(keyStrokeName)) {
+            return '2';
+        }
+        else if ("KP_Down".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_3".equals(keyStrokeName)) {
+            return '3';
+        }
+        else if ("KP_Next".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_4".equals(keyStrokeName)) {
+            return '4';
+        }
+        else if ("KP_Left".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_5".equals(keyStrokeName)) {
+            return '5';
+        }
+        else if ("KP_Begin".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_6".equals(keyStrokeName)) {
+            return '6';
+        }
+        else if ("KP_Right".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_7".equals(keyStrokeName)) {
+            return '7';
+        }
+        else if ("KP_Home".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_8".equals(keyStrokeName)) {
+            return '8';
+        }
+        else if ("KP_Up".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("KP_9".equals(keyStrokeName)) {
+            return '9';
+        }
+        else if ("KP_Prior".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Caps_Lock".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Multi_key".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("exclam".equals(keyStrokeName)) {
+            return '!';
+        }
+        else if ("onesuperior".equals(keyStrokeName)) {
+            return '¹';
+        }
+        else if ("exclamdown".equals(keyStrokeName)) {
+            return '¡';
+        }
+        else if ("quotedbl".equals(keyStrokeName)) {
+            return '"';
+        }
+        else if ("twosuperior".equals(keyStrokeName)) {
+            return '²';
+        }
+        else if ("oneeighth".equals(keyStrokeName)) {
+            return '⅛';
+        }
+        else if ("section".equals(keyStrokeName)) {
+            return '§';
+        }
+        else if ("threesuperior".equals(keyStrokeName)) {
+            return '³';
+        }
+        else if ("sterling".equals(keyStrokeName)) {
+            return '£';
+        }
+        else if ("dollar".equals(keyStrokeName)) {
+            return '$';
+        }
+        else if ("onequarter".equals(keyStrokeName)) {
+            return '¼';
+        }
+        else if ("currency".equals(keyStrokeName)) {
+            return '¤';
+        }
+        else if ("percent".equals(keyStrokeName)) {
+            return '%';
+        }
+        else if ("onehalf".equals(keyStrokeName)) {
+            return '½';
+        }
+        else if ("threeeighths".equals(keyStrokeName)) {
+            return '⅜';
+        }
+        else if ("ampersand".equals(keyStrokeName)) {
+            return '&';
+        }
+        else if ("threequarters".equals(keyStrokeName)) {
+            return '¾';
+        }
+        else if ("fiveeighths".equals(keyStrokeName)) {
+            return '⅝';
+        }
+        else if ("slash".equals(keyStrokeName)) {
+            return '/';
+        }
+        else if ("braceleft".equals(keyStrokeName)) {
+            return '{';
+        }
+        else if ("seveneighths".equals(keyStrokeName)) {
+            return '⅞';
+        }
+        else if ("parenleft".equals(keyStrokeName)) {
+            return '(';
+        }
+        else if ("bracketleft".equals(keyStrokeName)) {
+            return '[';
+        }
+        else if ("trademark".equals(keyStrokeName)) {
+            return '™';
+        }
+        else if ("parenright".equals(keyStrokeName)) {
+            return ')';
+        }
+        else if ("bracketright".equals(keyStrokeName)) {
+            return ']';
+        }
+        else if ("plusminus".equals(keyStrokeName)) {
+            return '±';
+        }
+        else if ("equal".equals(keyStrokeName)) {
+            return '=';
+        }
+        else if ("braceright".equals(keyStrokeName)) {
+            return '}';
+        }
+        else if ("ssharp".equals(keyStrokeName)) {
+            return 'ß';
+        }
+        else if ("question".equals(keyStrokeName)) {
+            return '?';
+        }
+        else if ("backslash".equals(keyStrokeName)) {
+            return '\\';
+        }
+        else if ("questiondown".equals(keyStrokeName)) {
+            return '¿';
+        }
+        else if ("acute".equals(keyStrokeName)) {
+            return '´';
+        }
+        else if ("dead_acute".equals(keyStrokeName)) {
+            return 0x0301;
+        }
+        else if ("grave".equals(keyStrokeName)) {
+            return '`';
+        }
+        else if ("dead_grave".equals(keyStrokeName)) {
+            return 0x0300;
+        }
+        else if ("dead_cedilla".equals(keyStrokeName)) {
+            return 0x0327;
+        }
+        else if ("dead_ogonek".equals(keyStrokeName)) {
+            return 0x0328;
+        }
+        else if ("at".equals(keyStrokeName)) {
+            return '@';
+        }
+        else if ("Greek_OMEGA".equals(keyStrokeName)) {
+            return 'Ω';
+        }
+        else if ("EuroSign".equals(keyStrokeName)) {
+            return '€';
+        }
+        else if ("paragraph".equals(keyStrokeName)) {
+            return 0x2029;
+        }
+        else if ("registered".equals(keyStrokeName)) {
+            return '®';
+        }
+        else if ("tslash".equals(keyStrokeName)) {
+            return 'ŧ';
+        }
+        else if ("Tslash".equals(keyStrokeName)) {
+            return 'Ŧ';
+        }
+        else if ("z".equals(keyStrokeName)) {
+            return getUpper ? 'Z' : 'z';
+        }
+        else if ("leftarrow".equals(keyStrokeName)) {
+            return '←';
+        }
+        else if ("yen".equals(keyStrokeName)) {
+            return '¥';
+        }
+        else if ("downarrow".equals(keyStrokeName)) {
+            return '↓';
+        }
+        else if ("uparrow".equals(keyStrokeName)) {
+            return '↑';
+        }
+        else if ("rightarrow".equals(keyStrokeName)) {
+            return '→';
+        }
+        else if ("idotless".equals(keyStrokeName)) {
+            return 'ı';
+        }
+        else if ("oslash".equals(keyStrokeName)) {
+            return 'ø';
+        }
+        else if ("Ooblique".equals(keyStrokeName)) {
+            return 'Ø';
+        }
+        else if ("thorn".equals(keyStrokeName)) {
+            return 'þ';
+        }
+        else if ("THORN".equals(keyStrokeName)) {
+            return 'Þ';
+        }
+        else if ("udiaeresis".equals(keyStrokeName)) {
+            return getUpper ? 'Ü' : 'ü';
+        }
+        else if ("Udiaeresis".equals(keyStrokeName)) {
+            return getUpper ? 'Ü' : 'ü';
+        }
+        else if ("dead_diaeresis".equals(keyStrokeName)) {
+            return 0x0308;
+        }
+        else if ("dead_abovering".equals(keyStrokeName)) {
+            return 0x030A;
+        }
+        else if ("plus".equals(keyStrokeName)) {
+            return '+';
+        }
+        else if ("asterisk".equals(keyStrokeName)) {
+            return '*';
+        }
+        else if ("asciitilde".equals(keyStrokeName)) {
+            return '~';
+        }
+        else if ("dead_tilde".equals(keyStrokeName)) {
+            return 0x0303;
+        }
+        else if ("dead_macron".equals(keyStrokeName)) {
+            return 0x0304;
+        }
+        else if ("ae".equals(keyStrokeName)) {
+            return 'æ';
+        }
+        else if ("AE".equals(keyStrokeName)) {
+            return 'Æ';
+        }
+        else if ("eth".equals(keyStrokeName)) {
+            return 'ð';
+        }
+        else if ("ETH".equals(keyStrokeName)) {
+            return 'Ð';
+        }
+        else if ("dstroke".equals(keyStrokeName)) {
+            return getUpper ? 'Đ' : 'đ';
+        }
+        else if ("ordfeminine".equals(keyStrokeName)) {
+            return 'ª';
+        }
+        else if ("eng".equals(keyStrokeName)) {
+            return 'ŋ';
+        }
+        else if ("ENG".equals(keyStrokeName)) {
+            return 'Ŋ';
+        }
+        else if ("hstroke".equals(keyStrokeName)) {
+            return 'ħ';
+        }
+        else if ("Hstroke".equals(keyStrokeName)) {
+            return 'Ħ';
+        }
+        else if ("kra".equals(keyStrokeName)) {
+            return 'ĸ';
+        }
+        else if ("odiaeresis".equals(keyStrokeName)) {
+            return 'ö';
+        }
+        else if ("Odiaeresis".equals(keyStrokeName)) {
+            return 'Ö';
+        }
+        else if ("dead_doubleacute".equals(keyStrokeName)) {
+            return 0x030B;
+        }
+        else if ("adiaeresis".equals(keyStrokeName)) {
+            return 'ä';
+        }
+        else if ("Adiaeresis".equals(keyStrokeName)) {
+            return 'Ä';
+        }
+        else if ("dead_caron".equals(keyStrokeName)) {
+            return 0x030C;
+        }
+        else if ("asciicircum".equals(keyStrokeName)) {
+            return '^';
+        }
+        else if ("dead_circumflex".equals(keyStrokeName)) {
+            return 0x0302;
+        }
+        else if ("degree".equals(keyStrokeName)) {
+            return '°';
+        }
+        else if ("notsign".equals(keyStrokeName)) {
+            return '¬';
+        }
+        else if ("numbersign".equals(keyStrokeName)) {
+            return '#';
+        }
+        else if ("apostrophe".equals(keyStrokeName)) {
+            return '\'';
+        }
+        else if ("dead_breve".equals(keyStrokeName)) {
+            return 0x0306;
+        }
+        else if ("y".equals(keyStrokeName)) {
+            return getUpper ? 'Y' : 'y';
+        }
+        else if ("guillemotleft".equals(keyStrokeName)) {
+            return '»';
+        }
+        else if ("guillemotright".equals(keyStrokeName)) {
+            return '«';
+        }
+        else if ("cent".equals(keyStrokeName)) {
+            return '¢';
+        }
+        else if ("copyright".equals(keyStrokeName)) {
+            return '©';
+        }
+        else if ("leftdoublequotemark".equals(keyStrokeName)) {
+            return '„';
+        }
+        else if ("rightdoublequotemark".equals(keyStrokeName)) {
+            return '“';
+        }
+        else if ("mu".equals(keyStrokeName)) {
+            return 'µ';
+        }
+        else if ("masculine".equals(keyStrokeName)) {
+            return 'º';
+        }
+        else if ("comma".equals(keyStrokeName)) {
+            return ',';
+        }
+        else if ("semicolon".equals(keyStrokeName)) {
+            return ';';
+        }
+        else if ("horizconnector".equals(keyStrokeName)) {
+            return '·';
+        }
+        else if ("multiply".equals(keyStrokeName)) {
+            return '×';
+        }
+        else if ("period".equals(keyStrokeName)) {
+            return '.';
+        }
+        else if ("colon".equals(keyStrokeName)) {
+            return ':';
+        }
+        else if ("periodcentered".equals(keyStrokeName)) {
+            return '…';
+        }
+        else if ("division".equals(keyStrokeName)) {
+            return '÷';
+        }
+        else if ("minus".equals(keyStrokeName)) {
+            return '-';
+        }
+        else if ("underscore".equals(keyStrokeName)) {
+            return '_';
+        }
+        else if ("dead_belowdot".equals(keyStrokeName)) {
+            return 0x0323;
+        }
+        else if ("dead_abovedot".equals(keyStrokeName)) {
+            return 0x0307;
+        }
+        else if ("eacute".equals(keyStrokeName)) {
+            return getUpper ? 'É' : 'é';
+        }
+        else if ("Eacute".equals(keyStrokeName)) {
+            return getUpper ? 'É' : 'é';
+        }
+        else if ("egrave".equals(keyStrokeName)) {
+            return getUpper ? 'È' : 'è';
+        }
+        else if ("Egrave".equals(keyStrokeName)) {
+            return getUpper ? 'È' : 'è';
+        }
+        else if ("ccedilla".equals(keyStrokeName)) {
+            return getUpper ? 'Ç' : 'ç';
+        }
+        else if ("Ccedilla".equals(keyStrokeName)) {
+            return getUpper ? 'Ç' : 'ç';
+        }
+        else if ("agrave".equals(keyStrokeName)) {
+            return getUpper ? 'À' : 'à';
+        }
+        else if ("Agrave".equals(keyStrokeName)) {
+            return getUpper ? 'À' : 'à';
+        }
+        else if ("lstroke".equals(keyStrokeName)) {
+            return getUpper ? 'Ł' : 'ł';
+        }
+        else if ("Lstroke".equals(keyStrokeName)) {
+            return getUpper ? 'Ł' : 'ł';
+        }
+        else if ("ugrave".equals(keyStrokeName)) {
+            return getUpper ? 'Ù' : 'ù';
+        }
+        else if ("Ugrave".equals(keyStrokeName)) {
+            return getUpper ? 'Ù' : 'ù';
+        }
+        else if ("igrave".equals(keyStrokeName)) {
+            return getUpper ? 'Ì' : 'ì';
+        }
+        else if ("Igrave".equals(keyStrokeName)) {
+            return getUpper ? 'Ì' : 'ì';
+        }
+        else if ("ograve".equals(keyStrokeName)) {
+            return getUpper ? 'Ò' : 'ò';
+        }
+        else if ("ograve".equals(keyStrokeName)) {
+            return getUpper ? 'Ò' : 'ò';
+        }
+        else if ("keyboard_type".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("keyboard_subtype".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("keyboard_functionkeys".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("kana_NU".equals(keyStrokeName)) {
+            return 'ヌ';
+        }
+        else if ("kana_FU".equals(keyStrokeName)) {
+            return 'フ';
+        }
+        else if ("kana_A".equals(keyStrokeName)) {
+            return 'ア';
+        }
+        else if ("kana_a".equals(keyStrokeName)) {
+            return 'ァ';
+        }
+        else if ("kana_U".equals(keyStrokeName)) {
+            return 'ウ';
+        }
+        else if ("kana_u".equals(keyStrokeName)) {
+            return 'ゥ';
+        }
+        else if ("kana_E".equals(keyStrokeName)) {
+            return 'エ';
+        }
+        else if ("kana_e".equals(keyStrokeName)) {
+            return 'ェ';
+        }
+        else if ("kana_O".equals(keyStrokeName)) {
+            return 'オ';
+        }
+        else if ("kana_o".equals(keyStrokeName)) {
+            return 'ォ';
+        }
+        else if ("kana_YA".equals(keyStrokeName)) {
+            return 'ヤ';
+        }
+        else if ("kana_ya".equals(keyStrokeName)) {
+            return 'ャ';
+        }
+        else if ("kana_YU".equals(keyStrokeName)) {
+            return 'ユ';
+        }
+        else if ("kana_yu".equals(keyStrokeName)) {
+            return 'ュ';
+        }
+        else if ("kana_YO".equals(keyStrokeName)) {
+            return 'ヨ';
+        }
+        else if ("kana_yo".equals(keyStrokeName)) {
+            return 'ョ';
+        }
+        else if ("kana_WA".equals(keyStrokeName)) {
+            return 'ワ';
+        }
+        else if ("kana_WO".equals(keyStrokeName)) {
+            return 'ヲ';
+        }
+        else if ("kana_HO".equals(keyStrokeName)) {
+            return 'ホ';
+        }
+        else if ("kana_HE".equals(keyStrokeName)) {
+            return 'ヘ';
+        }
+        else if ("kana_TA".equals(keyStrokeName)) {
+            return 'タ';
+        }
+        else if ("kana_TE".equals(keyStrokeName)) {
+            return 'テ';
+        }
+        else if ("kana_I".equals(keyStrokeName)) {
+            return 'イ';
+        }
+        else if ("kana_i".equals(keyStrokeName)) {
+            return 'ィ';
+        }
+        else if ("kana_SU".equals(keyStrokeName)) {
+            return 'ス';
+        }
+        else if ("kana_KA".equals(keyStrokeName)) {
+            return 'カ';
+        }
+        else if ("kana_N".equals(keyStrokeName)) {
+            return 'ン';
+        }
+        else if ("kana_NA".equals(keyStrokeName)) {
+            return 'ナ';
+        }
+        else if ("kana_NI".equals(keyStrokeName)) {
+            return 'ニ';
+        }
+        else if ("kana_RA".equals(keyStrokeName)) {
+            return 'ラ';
+        }
+        else if ("kana_SE".equals(keyStrokeName)) {
+            return 'セ';
+        }
+        else if ("voicedsound".equals(keyStrokeName)) {
+            return 0x3099;
+        }
+        else if ("semivoicedsound".equals(keyStrokeName)) {
+            return 0x309A;
+        }
+        else if ("kana_openingbracket".equals(keyStrokeName)) {
+            return 0x04A2;
+        }
+        else if ("kana_closingbracket".equals(keyStrokeName)) {
+            return 0x04A3;
+        }
+        else if ("kana_CHI".equals(keyStrokeName)) {
+            return 'チ';
+        }
+        else if ("kana_TO".equals(keyStrokeName)) {
+            return 'ト';
+        }
+        else if ("kana_SHI".equals(keyStrokeName)) {
+            return 'シ';
+        }
+        else if ("kana_HA".equals(keyStrokeName)) {
+            return 'ハ';
+        }
+        else if ("kana_KI".equals(keyStrokeName)) {
+            return 'キ';
+        }
+        else if ("kana_KU".equals(keyStrokeName)) {
+            return 'ク';
+        }
+        else if ("kana_MA".equals(keyStrokeName)) {
+            return 'マ';
+        }
+        else if ("kana_NO".equals(keyStrokeName)) {
+            return 'ノ';
+        }
+        else if ("kana_RI".equals(keyStrokeName)) {
+            return 'リ';
+        }
+        else if ("kana_RE".equals(keyStrokeName)) {
+            return 'レ';
+        }
+        else if ("kana_KE".equals(keyStrokeName)) {
+            return 'ケ';
+        }
+        else if ("Zenkaku_Hankaku".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Kanji".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("kana_MU".equals(keyStrokeName)) {
+            return 'ム';
+        }
+        else if ("kana_TSU".equals(keyStrokeName)) {
+            return 'ツ';
+        }
+        else if ("kana_tsu".equals(keyStrokeName)) {
+            return 'ッ';
+        }
+        else if ("kana_SA".equals(keyStrokeName)) {
+            return 'サ';
+        }
+        else if ("kana_SO".equals(keyStrokeName)) {
+            return 'ソ';
+        }
+        else if ("kana_HI".equals(keyStrokeName)) {
+            return 'ヒ';
+        }
+        else if ("kana_KO".equals(keyStrokeName)) {
+            return 'コ';
+        }
+        else if ("kana_MI".equals(keyStrokeName)) {
+            return 'ミ';
+        }
+        else if ("kana_MO".equals(keyStrokeName)) {
+            return 'モ';
+        }
+        else if ("kana_NE".equals(keyStrokeName)) {
+            return 'ネ';
+        }
+        else if ("kana_comma".equals(keyStrokeName)) {
+            return '､';
+        }
+        else if ("kana_RU".equals(keyStrokeName)) {
+            return 'ル';
+        }
+        else if ("kana_fullstop".equals(keyStrokeName)) {
+            return '｡';
+        }
+        else if ("kana_ME".equals(keyStrokeName)) {
+            return 'メ';
+        }
+        else if ("kana_conjunctive".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Henkan_Mode".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Hiragana_Katakana".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Katakana".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Romaji".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Muhenkan".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Eisu_toggle".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Eisu_toggle".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("F13".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Hangul".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else if ("Hangul_Hanja".equals(keyStrokeName)) {
+            return Character.UNASSIGNED;
+        }
+        else {
+            Console.traceln(Level.SEVERE, "unknown key stroke name " + keyStrokeName +
+                            " specified through keyboard map " + "for locale " + locale);
+
+            // if (shift)
+            // {
+            // System.err.println("    else if (\"" + keyStrokeName + "\".equals(keyStrokeName))");
+            // System.err.println("    {");
+            // System.err.println("      return shift ? '" +
+            // Character.toUpperCase(keyStrokeName.charAt(0)) +
+            // "' : '" + Character.toLowerCase(keyStrokeName.charAt(0)) + "';");
+            // System.err.println("    }");
+            // }
+            // else
+            // {
+            // System.err.println("    else if (\"" + keyStrokeName + "\".equals(keyStrokeName))");
+            // System.err.println("    {");
+            // System.err.println("      return '" + keyStrokeName + "';");
+            // System.err.println("    }");
+            // }
+            //
+            // return 0x0;
+            throw new IllegalArgumentException("no keyboard map available for locale " + locale);
+        }
+    }
+}
Index: trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyboardMapFactory.java
===================================================================
--- trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyboardMapFactory.java	(revision 922)
+++ trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/KeyboardMapFactory.java	(revision 922)
@@ -0,0 +1,36 @@
+package de.ugoe.cs.autoquest.keyboardmaps;
+
+import java.util.Locale;
+
+/**
+ * <p>
+ * Creates keyboard maps.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class KeyboardMapFactory {
+
+    /**
+     * <p>
+     * Constructor. Private to prevent initialization of this class.
+     * </p>
+     */
+    private KeyboardMapFactory() {
+    }
+
+    /**
+     * <p>
+     * Returns a {@link KeyboardMap} for the given {@link Locale}.
+     * </p>
+     *
+     * @param locale the locale
+     * @return the keyboard map
+     */
+    public static KeyboardMap createKeyboardMap(Locale locale) {
+        KeyboardMap keyboardMap = new KeyboardMap(locale);
+        keyboardMap.init();
+        return keyboardMap;
+    }
+}
Index: trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/VirtualKey.java
===================================================================
--- trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/VirtualKey.java	(revision 922)
+++ trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/VirtualKey.java	(revision 922)
@@ -0,0 +1,483 @@
+package de.ugoe.cs.autoquest.keyboardmaps;
+
+import java.awt.event.KeyEvent;
+
+/**
+ * <p>
+ * Enumeration of virtual keys.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public enum VirtualKey {
+
+    ENTER(KeyEvent.VK_ENTER),
+    BACK_SPACE(KeyEvent.VK_BACK_SPACE),
+    TAB(KeyEvent.VK_TAB),
+    CANCEL(KeyEvent.VK_CANCEL),
+    CLEAR(KeyEvent.VK_CLEAR),
+    SHIFT(KeyEvent.VK_SHIFT),
+    CONTROL(KeyEvent.VK_CONTROL),
+    ALT(KeyEvent.VK_ALT),
+    PAUSE(KeyEvent.VK_PAUSE),
+    CAPS_LOCK(KeyEvent.VK_CAPS_LOCK),
+    ESCAPE(KeyEvent.VK_ESCAPE),
+    SPACE(KeyEvent.VK_SPACE),
+    PAGE_UP(KeyEvent.VK_PAGE_UP),
+    PAGE_DOWN(KeyEvent.VK_PAGE_DOWN),
+    END(KeyEvent.VK_END),
+    HOME(KeyEvent.VK_HOME),
+
+    LEFT(KeyEvent.VK_LEFT),
+    UP(KeyEvent.VK_UP),
+    RIGHT(KeyEvent.VK_RIGHT),
+    DOWN(KeyEvent.VK_DOWN),
+
+    COMMA(KeyEvent.VK_COMMA),
+    MINUS(KeyEvent.VK_MINUS),
+    PERIOD(KeyEvent.VK_PERIOD),
+    SLASH(KeyEvent.VK_SLASH),
+
+    DIGIT_0(KeyEvent.VK_0),
+    DIGIT_1(KeyEvent.VK_1),
+    DIGIT_2(KeyEvent.VK_2),
+    DIGIT_3(KeyEvent.VK_3),
+    DIGIT_4(KeyEvent.VK_4),
+    DIGIT_5(KeyEvent.VK_5),
+    DIGIT_6(KeyEvent.VK_6),
+    DIGIT_7(KeyEvent.VK_7),
+    DIGIT_8(KeyEvent.VK_8),
+    DIGIT_9(KeyEvent.VK_9),
+
+    SEMICOLON(KeyEvent.VK_SEMICOLON),
+    EQUALS(KeyEvent.VK_EQUALS),
+
+    LETTER_A(KeyEvent.VK_A),
+    LETTER_B(KeyEvent.VK_B),
+    LETTER_C(KeyEvent.VK_C),
+    LETTER_D(KeyEvent.VK_D),
+    LETTER_E(KeyEvent.VK_E),
+    LETTER_F(KeyEvent.VK_F),
+    LETTER_G(KeyEvent.VK_G),
+    LETTER_H(KeyEvent.VK_H),
+    LETTER_I(KeyEvent.VK_I),
+    LETTER_J(KeyEvent.VK_J),
+    LETTER_K(KeyEvent.VK_K),
+    LETTER_L(KeyEvent.VK_L),
+    LETTER_M(KeyEvent.VK_M),
+    LETTER_N(KeyEvent.VK_N),
+    LETTER_O(KeyEvent.VK_O),
+    LETTER_P(KeyEvent.VK_P),
+    LETTER_Q(KeyEvent.VK_Q),
+    LETTER_R(KeyEvent.VK_R),
+    LETTER_S(KeyEvent.VK_S),
+    LETTER_T(KeyEvent.VK_T),
+    LETTER_U(KeyEvent.VK_U),
+    LETTER_V(KeyEvent.VK_V),
+    LETTER_W(KeyEvent.VK_W),
+    LETTER_X(KeyEvent.VK_X),
+    LETTER_Y(KeyEvent.VK_Y),
+    LETTER_Z(KeyEvent.VK_Z),
+
+    OPEN_BRACKET(KeyEvent.VK_OPEN_BRACKET),
+    BACK_SLASH(KeyEvent.VK_BACK_SLASH),
+    CLOSE_BRACKET(KeyEvent.VK_CLOSE_BRACKET),
+
+    NUMPAD_0(KeyEvent.VK_NUMPAD0),
+    NUMPAD_1(KeyEvent.VK_NUMPAD1),
+    NUMPAD_2(KeyEvent.VK_NUMPAD2),
+    NUMPAD_3(KeyEvent.VK_NUMPAD3),
+    NUMPAD_4(KeyEvent.VK_NUMPAD4),
+    NUMPAD_5(KeyEvent.VK_NUMPAD5),
+    NUMPAD_6(KeyEvent.VK_NUMPAD6),
+    NUMPAD_7(KeyEvent.VK_NUMPAD7),
+    NUMPAD_8(KeyEvent.VK_NUMPAD8),
+    NUMPAD_9(KeyEvent.VK_NUMPAD9),
+    MULTIPLY(KeyEvent.VK_MULTIPLY),
+    ADD(KeyEvent.VK_ADD),
+
+    SEPARATOR(KeyEvent.VK_SEPARATOR),
+
+    SUBTRACT(KeyEvent.VK_SUBTRACT),
+    DECIMAL(KeyEvent.VK_DECIMAL),
+    DIVIDE(KeyEvent.VK_DIVIDE),
+    DELETE(KeyEvent.VK_DELETE),
+    NUM_LOCK(KeyEvent.VK_NUM_LOCK),
+    SCROLL_LOCK(KeyEvent.VK_SCROLL_LOCK),
+
+    F1(KeyEvent.VK_F1),
+    F2(KeyEvent.VK_F2),
+    F3(KeyEvent.VK_F3),
+    F4(KeyEvent.VK_F4),
+    F5(KeyEvent.VK_F5),
+    F6(KeyEvent.VK_F6),
+    F7(KeyEvent.VK_F7),
+    F8(KeyEvent.VK_F8),
+    F9(KeyEvent.VK_F9),
+    F10(KeyEvent.VK_F10),
+    F11(KeyEvent.VK_F11),
+    F12(KeyEvent.VK_F12),
+    F13(KeyEvent.VK_F13),
+    F14(KeyEvent.VK_F14),
+    F15(KeyEvent.VK_F15),
+    F16(KeyEvent.VK_F16),
+    F17(KeyEvent.VK_F17),
+    F18(KeyEvent.VK_F18),
+    F19(KeyEvent.VK_F19),
+    F20(KeyEvent.VK_F20),
+    F21(KeyEvent.VK_F21),
+    F22(KeyEvent.VK_F22),
+    F23(KeyEvent.VK_F23),
+    F24(KeyEvent.VK_F24),
+
+    PRINTSCREEN(KeyEvent.VK_PRINTSCREEN),
+    INSERT(KeyEvent.VK_INSERT),
+    HELP(KeyEvent.VK_HELP),
+    META(KeyEvent.VK_META),
+
+    BACK_QUOTE(KeyEvent.VK_BACK_QUOTE),
+    QUOTE(KeyEvent.VK_QUOTE),
+
+    KP_UP(KeyEvent.VK_KP_UP),
+    KP_DOWN(KeyEvent.VK_KP_DOWN),
+    KP_LEFT(KeyEvent.VK_KP_LEFT),
+    KP_RIGHT(KeyEvent.VK_KP_RIGHT),
+
+    DEAD_GRAVE(KeyEvent.VK_DEAD_GRAVE),
+    DEAD_ACUTE(KeyEvent.VK_DEAD_ACUTE),
+    DEAD_CIRCUMFLEX(KeyEvent.VK_DEAD_CIRCUMFLEX),
+    DEAD_TILDE(KeyEvent.VK_DEAD_TILDE),
+    DEAD_MACRON(KeyEvent.VK_DEAD_MACRON),
+    DEAD_BREVE(KeyEvent.VK_DEAD_BREVE),
+    DEAD_ABOVEDOT(KeyEvent.VK_DEAD_ABOVEDOT),
+    DEAD_DIAERESIS(KeyEvent.VK_DEAD_DIAERESIS),
+    DEAD_ABOVERING(KeyEvent.VK_DEAD_ABOVERING),
+    DEAD_DOUBLEACUTE(KeyEvent.VK_DEAD_DOUBLEACUTE),
+    DEAD_CARON(KeyEvent.VK_DEAD_CARON),
+    DEAD_CEDILLA(KeyEvent.VK_DEAD_CEDILLA),
+    DEAD_OGONEK(KeyEvent.VK_DEAD_OGONEK),
+    DEAD_IOTA(KeyEvent.VK_DEAD_IOTA),
+    DEAD_VOICED_SOUND(KeyEvent.VK_DEAD_VOICED_SOUND),
+    DEAD_SEMIVOICED_SOUND(KeyEvent.VK_DEAD_SEMIVOICED_SOUND),
+
+    AMPERSAND(KeyEvent.VK_AMPERSAND),
+    ASTERISK(KeyEvent.VK_ASTERISK),
+    QUOTEDBL(KeyEvent.VK_QUOTEDBL),
+    LESS(KeyEvent.VK_LESS),
+    GREATER(KeyEvent.VK_GREATER),
+    BRACELEFT(KeyEvent.VK_BRACELEFT),
+    BRACERIGHT(KeyEvent.VK_BRACERIGHT),
+
+    AT(KeyEvent.VK_AT),
+    COLON(KeyEvent.VK_COLON),
+    CIRCUMFLEX(KeyEvent.VK_CIRCUMFLEX),
+    DOLLAR(KeyEvent.VK_DOLLAR),
+    EURO_SIGN(KeyEvent.VK_EURO_SIGN),
+    EXCLAMATION_MARK(KeyEvent.VK_EXCLAMATION_MARK),
+    INVERTED_EXCLAMATION_MARK(KeyEvent.VK_INVERTED_EXCLAMATION_MARK),
+    LEFT_PARENTHESIS(KeyEvent.VK_LEFT_PARENTHESIS),
+    NUMBER_SIGN(KeyEvent.VK_NUMBER_SIGN),
+    PLUS(KeyEvent.VK_PLUS),
+    RIGHT_PARENTHESIS(KeyEvent.VK_RIGHT_PARENTHESIS),
+    UNDERSCORE(KeyEvent.VK_UNDERSCORE),
+
+    WINDOWS(KeyEvent.VK_WINDOWS),
+    CONTEXT_MENU(KeyEvent.VK_CONTEXT_MENU),
+
+    FINAL(KeyEvent.VK_FINAL),
+    CONVERT(KeyEvent.VK_CONVERT),
+    NONCONVERT(KeyEvent.VK_NONCONVERT),
+    ACCEPT(KeyEvent.VK_ACCEPT),
+    MODECHANGE(KeyEvent.VK_MODECHANGE),
+    KANA(KeyEvent.VK_KANA),
+    KANJI(KeyEvent.VK_KANJI),
+    ALPHANUMERIC(KeyEvent.VK_ALPHANUMERIC),
+    KATAKANA(KeyEvent.VK_KATAKANA),
+    HIRAGANA(KeyEvent.VK_HIRAGANA),
+    FULL_WIDTH(KeyEvent.VK_FULL_WIDTH),
+    HALF_WIDTH(KeyEvent.VK_HALF_WIDTH),
+    ROMAN_CHARACTERS(KeyEvent.VK_ROMAN_CHARACTERS),
+    ALL_CANDIDATES(KeyEvent.VK_ALL_CANDIDATES),
+    PREVIOUS_CANDIDATE(KeyEvent.VK_PREVIOUS_CANDIDATE),
+    CODE_INPUT(KeyEvent.VK_CODE_INPUT),
+    JAPANESE_KATAKANA(KeyEvent.VK_JAPANESE_KATAKANA),
+    JAPANESE_HIRAGANA(KeyEvent.VK_JAPANESE_HIRAGANA),
+    JAPANESE_ROMAN(KeyEvent.VK_JAPANESE_ROMAN),
+    KANA_LOCK(KeyEvent.VK_KANA_LOCK),
+    INPUT_METHOD_ON_OFF(KeyEvent.VK_INPUT_METHOD_ON_OFF),
+
+    CUT(KeyEvent.VK_CUT),
+    COPY(KeyEvent.VK_COPY),
+    PASTE(KeyEvent.VK_PASTE),
+    UNDO(KeyEvent.VK_UNDO),
+    AGAIN(KeyEvent.VK_AGAIN),
+    FIND(KeyEvent.VK_FIND),
+    PROPS(KeyEvent.VK_PROPS),
+    STOP(KeyEvent.VK_STOP),
+    COMPOSE(KeyEvent.VK_COMPOSE),
+    ALT_GRAPH(KeyEvent.VK_ALT_GRAPH),
+    BEGIN(KeyEvent.VK_BEGIN),
+
+    UNDEFINED(KeyEvent.VK_UNDEFINED);
+
+    /*
+     * BAR(KeyEvent.VK_UNDEFINED),
+     * APOSTROPHE(KeyEvent.VK_UNDEFINED),
+     * QUESTIONMARK(KeyEvent.VK_UNDEFINED),
+     * DEGREE(KeyEvent.VK_UNDEFINED),
+     * HENKAN_MODE(KeyEvent.VK_UNDEFINED),
+     * MUHENKAN(KeyEvent.VK_UNDEFINED),
+     * EISU_TOGGLE(KeyEvent.VK_UNDEFINED),
+     * HANGUL(KeyEvent.VK_UNDEFINED),
+     * HANGUL_HANJA(KeyEvent.VK_UNDEFINED),
+     * EXECUTE(KeyEvent.VK_UNDEFINED);
+     */
+
+    /**
+     * <p>
+     * Virtual key code of the virtual key.
+     * </p>
+     */
+    private int virtualKeyCode = -1;
+
+    /**
+     * <p>
+     * Description of the virtual key.
+     * </p>
+     */
+    private String description;
+
+    /**
+     * <p>
+     * Constructor. Creates a new VirtualKey.
+     * </p>
+     * 
+     * @param virtualKeyCode
+     *            key code of the virtual key
+     */
+    private VirtualKey(int virtualKeyCode) {
+        this.virtualKeyCode = virtualKeyCode;
+        this.description = KeyEvent.getKeyText(this.virtualKeyCode);
+    }
+
+    /**
+     * <p>
+     * Returns the description of the virtual key.
+     * </p>
+     * 
+     * @return the description.
+     */
+    String getDescription() {
+        return description;
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is a combination key (e.g., shift, alt) or not.
+     * </p>
+     * 
+     * @return true, if the key is a combiniation key; false otherwise
+     */
+    public boolean isCombinationKey() {
+        switch (this)
+        {
+            case SHIFT:
+            case CONTROL:
+            case ALT:
+            case ALT_GRAPH:
+            case WINDOWS:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is a lock key (e.g., caps lock, num lock) or not.
+     * </p>
+     * 
+     * @return true, if the key is a lock key; false otherwise
+     */
+    public boolean isLockKey() {
+        switch (this)
+        {
+            case CAPS_LOCK:
+            case NUM_LOCK:
+            case SCROLL_LOCK:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is shift.
+     * </p>
+     * 
+     * @return true, if the key is shift; false otherwise
+     */
+    public boolean isShiftKey() {
+        switch (this)
+        {
+            case SHIFT:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the is an alt key.
+     * </p>
+     * 
+     * @return true, if the key is alt or altgr; false otherwise
+     */
+    public boolean isAltKey() {
+        switch (this)
+        {
+            case ALT:
+            case ALT_GRAPH:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is control.
+     * </p>
+     * 
+     * @return true, if the key is control; false otherwise
+     */
+    public boolean isControlKey() {
+        switch (this)
+        {
+            case CONTROL:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is the windows key.
+     * </p>
+     * 
+     * @return true, if the key is the windows key; false otherwise
+     */
+    public boolean isWindowsKey() {
+        switch (this)
+        {
+            case WINDOWS:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is the meta key.
+     * </p>
+     * 
+     * @return true, if the key is the meta key; false otherwise
+     */
+    public boolean isMetaKey() {
+        switch (this)
+        {
+            case META:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is a letter.
+     * </p>
+     * 
+     * @return true, if the key is a letter; false otherwise
+     */
+    public boolean isLetter() {
+        if (virtualKeyCode > -1) {
+            return Character.isLetter((char) virtualKeyCode);
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns whether the key is a digit.
+     * </p>
+     * 
+     * @return true, if the key is a digit; false otherwise
+     */
+    public boolean isDigit() {
+        if (virtualKeyCode > -1) {
+            return Character.isDigit(virtualKeyCode);
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * <p>
+     * Parses an {@link String} and returns the respective VirtualKey if possible.
+     * </p>
+     * 
+     * @param numberString
+     *            String representation of the virtual key
+     * @return created VirtualKey
+     * @throws IllegalArgumentException
+     *             thrown if there is no VirtualKey that correlates to string
+     */
+    public static VirtualKey parseVirtualKey(String string) {
+        int virtualKeyCode = Integer.parseInt(string);
+        for (VirtualKey key1 : VirtualKey.values()) {
+            if (key1.virtualKeyCode == virtualKeyCode) {
+                return key1;
+            }
+        }
+
+        throw new IllegalArgumentException("there is no virtual key with id " + string);
+    }
+
+    /**
+     * <p>
+     * Returns the VirtualKey associated with an integer.
+     * </p>
+     * 
+     * @param number
+     *            integer to which the according VirtualKey is returned
+     * @return the VirtualKey
+     * @throws IllegalArgumentException
+     *             thrown if there is no VirtualKey that correlates to number
+     */
+    public static VirtualKey valueOf(int number) {
+        for (VirtualKey virtualKey : VirtualKey.values()) {
+            if (virtualKey.virtualKeyCode == number) {
+                return virtualKey;
+            }
+        }
+
+        throw new IllegalArgumentException("there is no virtual key with number " + number);
+    }
+
+}
Index: trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/VirtualKeySynonyms.java
===================================================================
--- trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/VirtualKeySynonyms.java	(revision 922)
+++ trunk/autoquest-misc/src/main/java/de/ugoe/cs/autoquest/keyboardmaps/VirtualKeySynonyms.java	(revision 922)
@@ -0,0 +1,103 @@
+
+package de.ugoe.cs.autoquest.keyboardmaps;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Helper class to handle synonymous {@link VirtualKey}s.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+class VirtualKeySynonyms {
+
+    /**
+     * <p>
+     * Map of synonymous keys.
+     * </p>
+     */
+    private Map<Integer, List<VirtualKey>> synonyms = new HashMap<Integer, List<VirtualKey>>();
+
+    /**
+     * <p>
+     * Mapping of {@link VirtualKey}s to integer Ids.
+     * </p>
+     */
+    private Map<VirtualKey, Integer> keyIds = new HashMap<VirtualKey, Integer>();
+
+    /**
+     * <p>
+     * Adds a new synonymous key.
+     * </p>
+     * 
+     * @param keyId
+     *            id of the synonym
+     * @param virtualKey
+     *            the synonym
+     */
+    public void add(int keyId, VirtualKey virtualKey) {
+        List<VirtualKey> synonymList = synonyms.get(keyId);
+
+        if (synonymList == null) {
+            synonymList = new ArrayList<VirtualKey>();
+            synonyms.put(keyId, synonymList);
+        }
+
+        if (!synonymList.contains(virtualKey)) {
+            // ensure that the latest determined virtual keys are considered first
+            synonymList.add(0, virtualKey);
+        }
+
+        Integer existingKeyId = keyIds.get(virtualKey);
+
+        if ((existingKeyId != null) && (existingKeyId != keyId)) {
+            Console.traceln(Level.FINEST, "virtual key " + virtualKey + " is mapped to more " +
+                "than one key id (current is " + existingKeyId + ", new is " + keyId +
+                "). New key id will be used (" + keyId + ").");
+        }
+
+        keyIds.put(virtualKey, keyId);
+    }
+
+    /**
+     * <p>
+     * Returns whether a key is contained in the set of synonyms.
+     * </p>
+     * 
+     * @param keyId
+     *            id of the key
+     * @return true, if the key is contained; false otherwise
+     */
+    public boolean containsKey(int keyId) {
+        return synonyms.containsKey(keyId);
+    }
+
+    /**
+     * <p>
+     * Returns all synonyms known for a given key.
+     * </p>
+     * 
+     * @param keyId
+     *            the id of the key
+     * @return the synonyms
+     */
+    public VirtualKey[] getVirtualKeySynonyms(int keyId) {
+        List<VirtualKey> virtualKeys = synonyms.get(keyId);
+        if (virtualKeys != null) {
+            return virtualKeys.toArray(new VirtualKey[virtualKeys.size()]);
+        }
+        else {
+            Console.traceln(Level.WARNING, "no virtual key define for key id " + keyId);
+            return null;
+        }
+    }
+
+}
Index: trunk/autoquest-plugin-core-test/src/test/java/de/ugoe/cs/autoquest/plugin/PluginLoaderTest.java
===================================================================
--- trunk/autoquest-plugin-core-test/src/test/java/de/ugoe/cs/autoquest/plugin/PluginLoaderTest.java	(revision 922)
+++ trunk/autoquest-plugin-core-test/src/test/java/de/ugoe/cs/autoquest/plugin/PluginLoaderTest.java	(revision 922)
@@ -0,0 +1,212 @@
+package de.ugoe.cs.autoquest.plugin;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+import junitx.framework.ArrayAssert;
+
+import org.junit.*;
+
+import de.ugoe.cs.autoquest.plugin.PluginLoader;
+import de.ugoe.cs.autoquest.plugin.PluginLoaderException;
+import de.ugoe.cs.autoquest.plugin.QuestPlugin;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>PluginLoaderTest</code> contains tests for the class
+ * <code>{@link PluginLoader}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class PluginLoaderTest {
+	
+	@Test
+	public void testPluginLoader_1() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		assertNotNull(loader);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPluginLoader_2() throws Exception {
+		new PluginLoader(null);
+	}
+	
+	@Test(expected = java.lang.IllegalArgumentException.class)
+	public void testPluginLoader_3() throws Exception {
+		new PluginLoader(new File("testdata/de.ugoe.cs.autoquest.plugin.PluginLoaderTest/jfcmonitor.jar"));
+	}
+		
+	@Test
+	public void testCheckNameConformity_1() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "quest-plugin-jfc-1.0.jar";
+		boolean expected = true;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_2() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "quest-plugin-jf-c-1.0.jar";
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_3() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "quest-plugin-jfc.jar";
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_4() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "quest-plugi-jfc-1.0.jar";
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_5() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "quest-pluginjfc-1.0.jar";
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_6() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "quest-plugin-jfc-1-0.jar";
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_7() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "quest-plugin-jfc-1.0.nojar";
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_8() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = null;
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testCheckNameConformity_9() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		String filename = "";
+		boolean expected = false;
+		
+		boolean result = loader.checkNameConformity(filename);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testGetClassPathFromJar_1() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		File jarFile = new File("testdata/de.ugoe.cs.autoquest.plugin.PluginLoaderTest/jfcmonitor.jar");
+		
+		String[] expected = new String[]{ "file:" + jarFile.getParentFile().getAbsolutePath()+"/javahelperlib.jar" };
+				
+		String[] result = loader.getClassPathFromJar(jarFile);
+		
+		ArrayAssert.assertEquivalenceArrays(expected, result);
+	}
+	
+	@Test
+	public void testGetClassPathFromJar_2() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("."));
+		File jarFile = new File("testdata/de.ugoe.cs.autoquest.plugin.PluginLoaderTest/jmi.jar");
+		
+		String[] expected = new String[]{ };
+				
+		String[] result = loader.getClassPathFromJar(jarFile);
+		
+		ArrayAssert.assertEquivalenceArrays(expected, result);
+	}
+	
+	@Test 
+	public void testLoad_1() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("testdata/de.ugoe.cs.autoquest.plugin.PluginLoaderTest"));
+		
+		loader.load();
+		
+		Collection<QuestPlugin> plugins = loader.getPlugins();
+		
+		assertEquals(1, plugins.size());
+		QuestPlugin plugin = plugins.iterator().next();
+		assertNotNull(plugin);
+		assertEquals("Mock Plugin", plugin.getTitle());
+		assertEquals(Arrays.asList(new String[]{"de.ugoe.cs.autoquest.plugin.mock.commands"}), plugin.getCommandPackages());
+	}
+	
+	@Test 
+	public void testLoad_2() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("testdata/de.ugoe.cs.autoquest.plugin.PluginLoaderTestInvalid_1"));
+		
+		try {
+			loader.load();
+		} catch(PluginLoaderException e) {
+			e.getMessage().endsWith("not instance of QuestPlugin");
+		}
+	}
+	
+	@Test 
+	public void testLoad_3() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("testdata/de.ugoe.cs.autoquest.plugin.PluginLoaderTestInvalid_2"));
+		
+		try {
+			loader.load();
+		} catch(PluginLoaderException e) {
+			e.getMessage().startsWith("No class");
+		}
+	}
+	
+	@Test 
+	public void testLoad_4() throws Exception {
+		PluginLoader loader = new PluginLoader(new File("testdata/de.ugoe.cs.autoquest.plugin.PluginLoaderTestInvalid_3"));
+		
+		try {
+			loader.load();
+		} catch(PluginLoaderException e) {
+			e.getMessage().endsWith("Could not access");
+		}
+	}
+
+}
Index: trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoader.java
===================================================================
--- trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoader.java	(revision 922)
+++ trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoader.java	(revision 922)
@@ -0,0 +1,245 @@
+package de.ugoe.cs.autoquest.plugin;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+/**
+ * <p>
+ * This class provides the functionality to load QUEST plug-ins from a
+ * pre-defined folder.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class PluginLoader {
+
+	/**
+	 * <p>
+	 * Handle of the plug-in directory.
+	 * </p>
+	 */
+	private final File pluginDir;
+
+	/**
+	 * <p>
+	 * Collection of the loaded plug-ins.
+	 * </p>
+	 */
+	private final Collection<QuestPlugin> plugins;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new PluginLoader that can load plug-ins the
+	 * defined directory.
+	 * </p>
+	 * 
+	 * @param pluginDir
+	 *            handle of the directory; in case the handle is
+	 *            <code>null</code> or does not describe a directory, an
+	 *            {@link IllegalArgumentException} is thrown
+	 */
+	public PluginLoader(File pluginDir) {
+		if (pluginDir == null) {
+			throw new IllegalArgumentException(
+					"Parameter pluginDir must not be null!");
+		}
+		if (!pluginDir.isDirectory()) {
+			throw new IllegalArgumentException("File " + pluginDir.getPath()
+					+ " is not a directory");
+		}
+		this.pluginDir = pluginDir;
+		plugins = new LinkedList<QuestPlugin>();
+	}
+
+	/**
+	 * <p>
+	 * Loads plug-ins from {@link #pluginDir}.
+	 * </p>
+	 * 
+	 * @throws PluginLoaderException
+	 *             thrown if there is a problem loading a plug-in or updating
+	 *             the classpath
+	 */
+	public void load() throws PluginLoaderException {
+		File[] jarFiles = pluginDir.listFiles(new FilenameFilter() {
+			@Override
+			public boolean accept(File dir, String name) {
+				return checkNameConformity(name);
+			}
+		});
+
+		for (File jarFile : jarFiles) {
+			updateClassLoader(jarFile);
+
+			String pluginName = jarFile.getName().split("-")[2];
+			String pluginClassName = "de.ugoe.cs.autoquest.plugin." + pluginName
+					+ "." + pluginName.toUpperCase() + "Plugin";
+
+			Class<?> pluginClass = null;
+			try {
+				pluginClass = Class.forName(pluginClassName);
+			} catch (ClassNotFoundException e) {
+				throw new PluginLoaderException("No class '" + pluginClassName
+						+ "' found in " + pluginDir + "/" + jarFile.getName());
+			}
+			try {
+				QuestPlugin pluginObject = (QuestPlugin) pluginClass
+						.newInstance();
+				plugins.add(pluginObject);
+			} catch (InstantiationException e) {
+				throw new PluginLoaderException("Could not instantiate "
+						+ pluginClassName);
+			} catch (IllegalAccessException e) {
+				throw new PluginLoaderException("Could not access "
+						+ pluginClassName);
+			} catch (ClassCastException e) {
+				throw new PluginLoaderException("Class " + pluginClassName
+						+ " not instance of QuestPlugin");
+			}
+		}
+	}
+
+	/**
+	 * <p>
+	 * Retrieves the classpath from a Jar file's MANIFEST.
+	 * </p>
+	 * 
+	 * @throws IOException
+	 * @throws FileNotFoundException
+	 */
+	protected String[] getClassPathFromJar(File jarFile) {
+		String[] classPath;
+
+		JarInputStream jarInputStream = null;
+		Manifest manifest = null;
+		try {
+		    FileInputStream fileStream = new FileInputStream(jarFile);
+		    try {
+		        jarInputStream = new JarInputStream(fileStream);
+		        manifest = jarInputStream.getManifest();
+		    } finally {
+		        jarInputStream.close();
+		        fileStream.close();
+		    }
+		} catch (FileNotFoundException e) {
+			throw new AssertionError(
+					"FileNotFoundException should be impossible!");
+		} catch (IOException e) {
+			throw new PluginLoaderException(e);
+		}
+
+		String jarClassPath = manifest.getMainAttributes().getValue(
+				"Class-Path");
+
+		if (jarClassPath != null) {
+			String[] jarClassPathElements = jarClassPath.split(" ");
+			classPath = new String[jarClassPathElements.length];
+			for (int i = 0; i < jarClassPathElements.length; i++) {
+				classPath[i] = "file:"
+						+ jarFile.getParentFile().getAbsolutePath() + "/"
+						+ jarClassPathElements[i];
+			}
+			try {
+				jarInputStream.close();
+			} catch (IOException e) {
+				throw new PluginLoaderException(e);
+			}
+		} else {
+			classPath = new String[] {};
+		}
+		return classPath;
+	}
+
+	/**
+	 * <p>
+	 * Updates the classpath of the {@link ClassLoader} to include the plug-in
+	 * jar as well as further libraries required by the plug-in jar as defined
+	 * in the <code>Class-Path</code> section of its manifest.
+	 * </p>
+	 * 
+	 * @param jarFile
+	 *            handle of the plug-in jar file
+	 * @throws PluginLoaderException
+	 *             thrown if there is a problem updating the class loader or
+	 *             loading the plug-in jar
+	 */
+	private void updateClassLoader(File jarFile) throws PluginLoaderException {
+		String[] classPath = getClassPathFromJar(jarFile);
+		URLClassLoader classLoader = (URLClassLoader) ClassLoader
+				.getSystemClassLoader();
+		Method method;
+
+		try {
+			method = URLClassLoader.class.getDeclaredMethod("addURL",
+					new Class[] { URL.class });
+		} catch (SecurityException e) {
+			throw new PluginLoaderException(
+					"addURL method of URLClassLoader not accessible via reflection.");
+		} catch (NoSuchMethodException e) {
+			throw new AssertionError(
+					"URLClassLoader does not have addURL method. Should be impossible!!");
+		}
+		method.setAccessible(true);
+
+		try {
+			method.invoke(
+					classLoader,
+					new Object[] { new URL("file:" + jarFile.getAbsoluteFile()) });
+			for (String element : classPath) {
+				method.invoke(classLoader, new Object[] { new URL(element) });
+			}
+		} catch (IllegalArgumentException e) {
+			throw new AssertionError(
+					"Illegal arguments for addURL method. Should be impossible!!");
+		} catch (MalformedURLException e) {
+			throw new PluginLoaderException(e);
+		} catch (IllegalAccessException e) {
+			throw new PluginLoaderException(
+					"addURL method of URLClassLoader not accessible via reflection.");
+		} catch (InvocationTargetException e) {
+			throw new PluginLoaderException(e);
+		}
+	}
+
+	/**
+	 * <p>
+	 * Checks if the name of a file indicates that it defines a QUEST plug-in.
+	 * The structure of valid plug-in filenames is
+	 * <code>quest-plugin-%PLUGIN_NAME%-version.jar</code>, where
+	 * <code>%PLUGIN_NAME%</code> is replaced by the name of the plug-in. Note
+	 * that plug-in names must not contain any dashes.
+	 * </p>
+	 * 
+	 * @param filename
+	 *            filename that is checked
+	 * @return true if filename matches pattern of QUEST plug-in; false
+	 *         otherwise
+	 */
+	protected boolean checkNameConformity(String filename) {
+		if (filename == null) {
+			return false;
+		}
+		return filename.startsWith("quest-plugin-") && !filename.startsWith("quest-plugin-core")
+				&&
+				((filename.split("-").length == 4 && filename.endsWith(".jar")) ||
+				  filename.split("-").length == 5 && filename.endsWith("SNAPSHOT.jar"));
+	}
+	
+	public Collection<QuestPlugin> getPlugins() {
+		return Collections.unmodifiableCollection(plugins);
+	}
+}
Index: trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoaderException.java
===================================================================
--- trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoaderException.java	(revision 922)
+++ trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoaderException.java	(revision 922)
@@ -0,0 +1,37 @@
+package de.ugoe.cs.autoquest.plugin;
+
+/**
+ * <p>
+ * This exception is thrown in case there is an error during the loading of
+ * QUEST plugins. Note that the failures during the loading may result in an
+ * invalid classpath and this exception should, therefore, be treated with
+ * appropriate care.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class PluginLoaderException extends RuntimeException {
+
+	/**
+	 * @see RuntimeException#RuntimeException(String)
+	 */
+	public PluginLoaderException(String msg) {
+		super(msg);
+	}
+	
+	/**
+	 * @see RuntimeException#RuntimeException(Throwable)
+	 */
+	public PluginLoaderException(Throwable throwable) {
+		super(throwable);
+	}
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+}
Index: trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/QuestPlugin.java
===================================================================
--- trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/QuestPlugin.java	(revision 922)
+++ trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/QuestPlugin.java	(revision 922)
@@ -0,0 +1,37 @@
+package de.ugoe.cs.autoquest.plugin;
+
+import java.util.List;
+
+/**
+ * <p>
+ * Interface for QUEST plug-ins.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface QuestPlugin {
+
+	/**
+	 * <p>
+	 * Title of the plug-in.
+	 * </p>
+	 * 
+	 * @return the title
+	 */
+	public String getTitle();
+
+	/**
+	 * <p>
+	 * {@link List} of {@link String}s that contain the commands defined by this
+	 * plug-in. The List is immutable.
+	 * </p>
+	 * <p>
+	 * Consult the de.ugoe.cs.utils.console package of the java-utils project
+	 * for more information.
+	 * </p>
+	 * 
+	 * @return the command packages
+	 */
+	public List<String> getCommandPackages();
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/EFGModelGenerator.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/EFGModelGenerator.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/EFGModelGenerator.java	(revision 922)
@@ -0,0 +1,133 @@
+package de.ugoe.cs.autoquest.plugin.guitar;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.plugin.guitar.eventcore.GUITAREventTarget;
+import de.ugoe.cs.autoquest.plugin.guitar.eventcore.GUITAREventType;
+import de.ugoe.cs.autoquest.plugin.guitar.eventcore.GUITARReplayable;
+import de.ugoe.cs.autoquest.usageprofiles.DeterministicFiniteAutomaton;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import edu.umd.cs.guitar.model.GUITARConstants;
+import edu.umd.cs.guitar.model.IO;
+import edu.umd.cs.guitar.model.data.EFG;
+import edu.umd.cs.guitar.model.data.EventGraphType;
+import edu.umd.cs.guitar.model.data.EventType;
+
+/**
+ * <p>
+ * Provides functionality to generates models defined in the package
+ * de.ugoe.cs.autoquest.usageprofiles from EFGs.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class EFGModelGenerator {
+
+	/**
+	 * <p>
+	 * Generates a {@link FirstOrderMarkovModel} from an EFG. In the generated
+	 * model, all following events are equally possible, i.e., the model is
+	 * equal to a {@link DeterministicFiniteAutomaton}. 
+	 * </p>
+	 * 
+	 * @param efgFileName
+	 *            name of the EFG file
+	 * @return model generated from the EFG
+	 */
+	public FirstOrderMarkovModel efgToFirstOrderMarkovModel(String efgFileName) {
+		EFG efg = (EFG) IO.readObjFromFile(efgFileName, EFG.class);
+
+		Collection<List<Event>> subsequences = generateEdgeSequences(efg);
+		FirstOrderMarkovModel model = new FirstOrderMarkovModel(new Random());
+		model.train(subsequences);
+		return model;
+	}
+
+	/**
+	 * <p>
+	 * Generates a {@link DeterministicFiniteAutomaton} from an EFG.
+	 * </p>
+	 * 
+	 * @param efgFileName
+	 *            name of the EFG file
+	 * @return model generated from the EFG
+	 */
+	public DeterministicFiniteAutomaton efgToDeterministicFiniteAutomaton(
+			String efgFileName) {
+		EFG efg = (EFG) IO.readObjFromFile(efgFileName, EFG.class);
+
+		Collection<List<Event>> subsequences = generateEdgeSequences(efg);
+		DeterministicFiniteAutomaton model = new DeterministicFiniteAutomaton(
+				new Random());
+		model.train(subsequences);
+		return model;
+	}
+
+	/**
+	 * <p>
+	 * Extracts the graph structure from the EFG. The result is a set of
+	 * sequences, where each sequence has length two and represents an edge in
+	 * the EFG.
+	 * </p>
+	 * 
+	 * @param efg
+	 *            EFG for which the edge sequence set is generated
+	 * @return edge sequence set
+	 */
+	private Collection<List<Event>> generateEdgeSequences(EFG efg) {
+		List<Event> events = createEvents(efg);
+		/*
+		 * getEventGraph returns an adjacency matrix, i.e., a square matrix of
+		 * efgEvents.size(), where a 1 in row i, column j means an edge
+		 * efgEvents.get(i)->efgEvents.get(j) exists.
+		 */
+		EventGraphType efgGraph = efg.getEventGraph();
+		Collection<List<Event>> subsequences = new LinkedList<List<Event>>();
+
+		int efgSize = events.size();
+		for (int row = 0; row < efgSize; row++) {
+			for (int col = 0; col < efgSize; col++) {
+				int relation = efgGraph.getRow().get(row).getE().get(col);
+				// otherEvent is followed by currentEvent
+				if (relation != GUITARConstants.NO_EDGE) {
+					List<Event> edge = new LinkedList<Event>();
+					edge.add(events.get(row));
+					edge.add(events.get(col));
+					subsequences.add(edge);
+				}
+			}
+		}
+		return subsequences;
+	}
+
+	/**
+	 * <p>
+	 * Extracts creates {@link EFGEvent} for every event contained in the EFG.
+	 * </p>
+	 * 
+	 * @param efg
+	 *            EFG for which the events are created
+	 * @return list of events
+	 */
+	private List<Event> createEvents(EFG efg) {
+		List<EventType> efgEvents = efg.getEvents().getEvent();
+		List<Event> myEvents = new ArrayList<Event>(efgEvents.size());
+		for (EventType event : efgEvents) {
+			/*
+			 * the widgetId and eventId are only hash values, the
+			 * "interpretation" is found in the GUI file.
+			 */
+		    GUITAREventType type = new GUITAREventType(event.getEventId());
+		    GUITAREventTarget target = new GUITAREventTarget(event.getWidgetId());
+		    Event myEvent = new Event(type, target);
+		    myEvent.addReplayable(new GUITARReplayable(event.getEventId()));
+		}
+		return myEvents;
+	}
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/EFGReplayDecorator.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/EFGReplayDecorator.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/EFGReplayDecorator.java	(revision 922)
@@ -0,0 +1,95 @@
+package de.ugoe.cs.autoquest.plugin.guitar;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * {@link IReplayDecorator} for replays generated for the GUITAR suite.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class EFGReplayDecorator implements IReplayDecorator {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * The instance of the {@link EFGReplayDecorator} (implemented as
+	 * singleton).
+	 * </p>
+	 */
+	transient private static EFGReplayDecorator theInstance;
+
+	/**
+	 * <p>
+	 * Constructor. Private to guarantee that only one instance of the replay
+	 * generator exists.
+	 * </p>
+	 */
+	private EFGReplayDecorator() {
+	};
+
+	/**
+	 * <p>
+	 * Returns the instance of the MFCReplayDecorator.
+	 * </p>
+	 * 
+	 * @return instance of the MFCReplayDecorator.
+	 */
+	public static EFGReplayDecorator getInstance() {
+		if (theInstance == null) {
+			theInstance = new EFGReplayDecorator();
+		}
+		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 + "<TestCase>" + StringTools.ENDLINE;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.IReplayDecorator#getFooter()
+	 */
+	@Override
+	public String getFooter() {
+		return "</TestCase>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.IReplayDecorator#getSessionHeader(int)
+	 */
+	@Override
+	public String getSessionHeader(int sessionId) {
+		return "";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.IReplayDecorator#getSessionFooter(int)
+	 */
+	@Override
+	public String getSessionFooter(int sessionId) {
+		return "";
+	}
+
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/GUITARPlugin.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/GUITARPlugin.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/GUITARPlugin.java	(revision 922)
@@ -0,0 +1,46 @@
+package de.ugoe.cs.autoquest.plugin.guitar;
+
+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 GUITAR plug-in.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class GUITARPlugin implements QuestPlugin {
+
+	/**
+	 * <p>
+	 * The command packages of this plug-in.
+	 * </p>
+	 */
+	private final static String[] commandPackages = new String[] { "de.ugoe.cs.autoquest.plugin.guitar.commands" };
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.plugin.QuestPlugin#getTitle()
+	 */
+	@Override
+	public String getTitle() {
+		return "GUITAR-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-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/GUITARTestCaseParser.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/GUITARTestCaseParser.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/GUITARTestCaseParser.java	(revision 922)
@@ -0,0 +1,113 @@
+package de.ugoe.cs.autoquest.plugin.guitar;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.plugin.guitar.eventcore.GUITAREventTarget;
+import de.ugoe.cs.autoquest.plugin.guitar.eventcore.GUITAREventType;
+import de.ugoe.cs.autoquest.plugin.guitar.eventcore.GUITARReplayable;
+import edu.umd.cs.guitar.model.IO;
+import edu.umd.cs.guitar.model.data.EFG;
+import edu.umd.cs.guitar.model.data.EventType;
+import edu.umd.cs.guitar.model.data.StepType;
+import edu.umd.cs.guitar.model.data.TestCase;
+
+/**
+ * <p>
+ * Parser for GUITAR test case files.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class GUITARTestCaseParser {
+
+	/**
+	 * <p>
+	 * Name of the EFG file for the application the test cases that are parsed
+	 * are generated for.
+	 * </p>
+	 */
+	private String efgFileName = null;
+
+	/**
+	 * <p>
+	 * Internal handle to the parsed EFG.
+	 * </p>
+	 */
+	private EFG efg = null;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new GUITARTestCaseParser. No EFG file is
+	 * associated with this parser.
+	 * </p>
+	 */
+	public GUITARTestCaseParser() {
+
+	}
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new GUITARTestCaseParser.
+	 * </p>
+	 * 
+	 * @param efgFileName
+	 *            EFG file associated with the test cases that are parsed.
+	 */
+	public GUITARTestCaseParser(String efgFileName) {
+		this.efgFileName = efgFileName;
+	}
+
+	/**
+	 * <p>
+	 * Parses a GUITAR test case file and returns its contents as an event
+	 * sequence.
+	 * </p>
+	 * 
+	 * @param testcaseFile
+	 *            file that is parsed
+	 * @return event sequence describing the test case
+	 */
+	public List<Event> parseTestCaseFile(File testcaseFile) {
+		TestCase testcase = (TestCase) IO.readObjFromFile(
+				testcaseFile.getAbsolutePath(), TestCase.class);
+		List<StepType> steps = testcase.getStep();
+		List<Event> sequence = new LinkedList<Event>();
+		for (StepType step : steps) {
+			String eventId = step.getEventId();
+			GUITAREventType type = new GUITAREventType(eventId);
+			GUITAREventTarget target = new GUITAREventTarget(getWidgetId(eventId));
+			Event event = new Event(type, target);
+			event.addReplayable(new GUITARReplayable(eventId));
+			sequence.add(event);
+		}
+		return sequence;
+	}
+
+	/**
+	 * <p>
+	 * If {@link #efgFileName} is specified, this function retrieves the
+	 * widgetId of the widget the event with id eventId belongs to from the EFG.
+	 * </p>
+	 * 
+	 * @param eventId
+	 * @return widgetId of the associated widget; null if {@link #efgFileName}
+	 *         ==null or no widgetId for the event is found in the EFG
+	 */
+	private String getWidgetId(String eventId) {
+		if (eventId != null && efgFileName != null) {
+			if (efg == null) {
+				efg = (EFG) IO.readObjFromFile(efgFileName, EFG.class);
+			}
+			for (EventType event : efg.getEvents().getEvent()) {
+				if (eventId.equals(event.getEventId())) {
+					return event.getWidgetId();
+				}
+			}
+		}
+		return null;
+	}
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgTestCasesToSequences.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgTestCasesToSequences.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgTestCasesToSequences.java	(revision 922)
@@ -0,0 +1,83 @@
+package de.ugoe.cs.autoquest.plugin.guitar.commands;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.plugin.guitar.GUITARTestCaseParser;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to load a set of sequences from a set of GUITAR test cases.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDefgTestCasesToSequences implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String foldername;
+		String sequencesName;
+		String efgFileName = null;
+		try {
+			foldername = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			if (parameters.size() >= 3) {
+				efgFileName = (String) parameters.get(2);
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		File folder = new File(foldername);
+		
+		File[] testcaseFiles = folder.listFiles( new FilenameFilter() {
+			@Override
+			public boolean accept(File dir, String name) {
+				return name.endsWith(".tst");
+			}
+		});
+		Collection<List<Event>> sequences = new LinkedList<List<Event>>();
+		GUITARTestCaseParser parser;
+		if (efgFileName == null) {
+			parser = new GUITARTestCaseParser();
+		} else {
+			parser = new GUITARTestCaseParser(efgFileName);
+		}
+		for (File testcaseFile : testcaseFiles) {
+			Console.traceln(Level.INFO, "Loading from file "
+					+ testcaseFile.getAbsolutePath());
+			sequences.add(parser.parseTestCaseFile(testcaseFile));
+		}
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "efgTestCasesToSequences <directory> <sequencesName> {<guiFileName>}";
+	}
+
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgToDFA.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgToDFA.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgToDFA.java	(revision 922)
@@ -0,0 +1,53 @@
+package de.ugoe.cs.autoquest.plugin.guitar.commands;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.plugin.guitar.EFGModelGenerator;
+import de.ugoe.cs.autoquest.usageprofiles.DeterministicFiniteAutomaton;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to that loads an EFG and creates Deterministic Finite Automaton (DFA)
+ * with the same structure.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDefgToDFA implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		String modelname;
+		try {
+			filename = (String) parameters.get(0);
+			modelname = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		EFGModelGenerator modelGenerator = new EFGModelGenerator();
+		DeterministicFiniteAutomaton model = modelGenerator
+				.efgToDeterministicFiniteAutomaton(filename);
+		GlobalDataContainer.getInstance().addData(modelname, model);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "efgToDFA <filename> <modelname>";
+	}
+
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgToMM.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgToMM.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/commands/CMDefgToMM.java	(revision 922)
@@ -0,0 +1,53 @@
+package de.ugoe.cs.autoquest.plugin.guitar.commands;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.plugin.guitar.EFGModelGenerator;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to that loads an EFG and creates a first-order Markov model with the
+ * same structure.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDefgToMM implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		String modelname;
+		try {
+			filename = (String) parameters.get(0);
+			modelname = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		EFGModelGenerator modelGenerator = new EFGModelGenerator();
+		FirstOrderMarkovModel model = modelGenerator
+				.efgToFirstOrderMarkovModel(filename);
+		GlobalDataContainer.getInstance().addData(modelname, model);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "efgToMM <filename> <modelname>";
+	}
+
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITAREventTarget.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITAREventTarget.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITAREventTarget.java	(revision 922)
@@ -0,0 +1,104 @@
+
+package de.ugoe.cs.autoquest.plugin.guitar.eventcore;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+
+/**
+ * <p>
+ * Event target for GUITAR events. The targets are described by a widgetId.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class GUITAREventTarget implements IEventTarget {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Id of the widget, which can be looked up in a GUITAR .gui file.
+     * </p>
+     */
+    private String widgetId;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link GUITAREventTarget}.
+     * </p>
+     * 
+     * @param widgetId
+     *            widget id of the target
+     */
+    public GUITAREventTarget(String widgetId) {
+        this.widgetId = widgetId;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getPlatform()
+     */
+    @Override
+    public String getPlatform() {
+        return "GUITAR";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getStringIdentifier()
+     */
+    @Override
+    public String getStringIdentifier() {
+        return this.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return widgetId;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof GUITAREventTarget) {
+            if (widgetId != null) {
+                return widgetId.equals(((GUITAREventTarget) obj).widgetId);
+            }
+            else {
+                return ((GUITAREventTarget) obj).widgetId == null;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hash = 13;
+        if (widgetId != null) {
+            hash = widgetId.hashCode();
+        }
+        return hash;
+    }
+
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITAREventType.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITAREventType.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITAREventType.java	(revision 922)
@@ -0,0 +1,94 @@
+
+package de.ugoe.cs.autoquest.plugin.guitar.eventcore;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+/**
+ * <p>
+ * Event type of GUITAR events. The types are defined by eventIds.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class GUITAREventType implements IEventType {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * GUITAR eventId of the event type.
+     * </p>
+     */
+    String guitarEventId;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link GUITAREventType}.
+     * </p>
+     * 
+     * @param eventId
+     *            eventId of the event type
+     */
+    public GUITAREventType(String eventId) {
+        this.guitarEventId = eventId;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventType#getName()
+     */
+    @Override
+    public String getName() {
+        return "GUITAREventType";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return guitarEventId;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof GUITAREventType) {
+            if (guitarEventId != null) {
+                return guitarEventId.equals(((GUITAREventType) obj).guitarEventId);
+            }
+            else {
+                return ((GUITAREventType) obj).guitarEventId == null;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hash = 37;
+        if (guitarEventId != null) {
+            hash = guitarEventId.hashCode();
+        }
+        return hash;
+    }
+
+}
Index: trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITARReplayable.java
===================================================================
--- trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITARReplayable.java	(revision 922)
+++ trunk/autoquest-plugin-guitar/src/main/java/de/ugoe/cs/autoquest/plugin/guitar/eventcore/GUITARReplayable.java	(revision 922)
@@ -0,0 +1,68 @@
+package de.ugoe.cs.autoquest.plugin.guitar.eventcore;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.autoquest.eventcore.IReplayable;
+import de.ugoe.cs.autoquest.plugin.guitar.EFGReplayDecorator;
+import de.ugoe.cs.util.StringTools;
+
+/**
+ * <p>
+ * {@link IReplayable} used to generate test cases for the GUITAR suite.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class GUITARReplayable implements IReplayable {
+
+    /**
+     * <p>
+     * EventId in the EFG and GUI files.
+     * </p>
+     */
+    String eventId;
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new {@link GUITARReplayable}.
+     * </p>
+     * 
+     * @param eventId
+     */
+    public GUITARReplayable(String eventId) {
+        this.eventId = eventId;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getReplay()
+     */
+    @Override
+    public String getReplay() {
+        StringBuilder replay = new StringBuilder();
+        replay.append("<Step>" + StringTools.ENDLINE);
+        replay.append("<EventId>" + eventId + "</EventId>" + StringTools.ENDLINE);
+        replay.append("<ReachingStep>false</ReachingStep>" + StringTools.ENDLINE);
+        replay.append("</Step>" + StringTools.ENDLINE);
+        return replay.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getDecorator()
+     */
+    @Override
+    public IReplayDecorator getDecorator() {
+        return EFGReplayDecorator.getInstance();
+    }
+
+}
Index: trunk/autoquest-plugin-jfc-test/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-plugin-jfc-test/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-plugin-jfc-test/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCLogParserTest.java
===================================================================
--- trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCLogParserTest.java	(revision 922)
+++ trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCLogParserTest.java	(revision 922)
@@ -0,0 +1,98 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.plugin.jfc.JFCLogParser;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class JFCLogParserTest {
+
+    /**
+     *
+     */
+    @Before
+    public void setUp() {
+        new TextConsole(Level.FINEST);
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void test() throws Exception {
+        JFCLogParser parser = new JFCLogParser(null);
+        parser.parseFile(new File(ClassLoader.getSystemResource("trace.xml").getFile()));
+        Collection<List<Event>> events = parser.getSequences();
+
+        assertNotNull(events);
+        assertTrue(events.size() > 0);
+
+        System.err.println("{");
+        for (List<Event> session : events) {
+            System.err.println("  {");
+            for (Event event : session) {
+                System.err.print("    ");
+                System.err.print(event);
+                System.err.println(",");
+            }
+            System.err.println("  }");
+        }
+        System.err.println("}");
+        System.err.println("\n\n");
+
+        GUIModel guiModel = parser.getGuiModel();
+        assertNotNull(guiModel);
+
+        for (IGUIElement root : guiModel.getRootElements()) {
+            dumpGUIElement(root, guiModel, "");
+        }
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param root
+     * @param guiModel
+     */
+    private void dumpGUIElement(IGUIElement guiElement, GUIModel guiModel, String indent) {
+        assertTrue(guiElement instanceof JFCGUIElement);
+
+        System.err.print(indent);
+        System.err.print(guiElement);
+
+        List<IGUIElement> children = guiModel.getChildren(guiElement);
+
+        if ((children != null) && (children.size() > 0)) {
+            System.err.println(" {");
+
+            for (IGUIElement child : children) {
+                dumpGUIElement(child, guiModel, indent + "  ");
+            }
+
+            System.err.print(indent);
+            System.err.print("}");
+        }
+
+        System.err.println();
+    }
+
+}
Index: trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDCalculatorTest.java
===================================================================
--- trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDCalculatorTest.java	(revision 922)
+++ trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDCalculatorTest.java	(revision 922)
@@ -0,0 +1,303 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.mockito.Mockito.*;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.plugin.jfc.JFCLogParser;
+import de.ugoe.cs.autoquest.plugin.jfc.JFCReplayIDCalculator;
+import de.ugoe.cs.autoquest.plugin.jfc.JFCReplayIDValidator;
+import de.ugoe.cs.autoquest.plugin.jfc.eventcore.JFCEventId;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElementSpec;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * The class <code>EFGReplayIDCalculatorTest</code> contains tests for the class
+ * {@link <code>EFGReplayIDCalculator</code>}
+ *
+ * @pattern JUnit Test Case
+ *
+ * @author fabian.glaser
+ *
+ * @version $Revision$
+ */
+public class JFCReplayIDCalculatorTest extends TestCase {
+	
+	Set<String> knownIDs = new HashSet<String>();
+
+	/**
+	 * Construct new test instance
+	 *
+	 * @param name the test name
+	 */
+	public JFCReplayIDCalculatorTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Run the String calculateReplayID(JFCEvent) method test.
+	 *
+	 * @throws Exception
+	 *
+	 * @generatedBy CodePro at 7/30/12 4:58 PM
+	 */
+	@Test
+	public void testCalculateReplayIDwithEvent()
+		throws Exception {
+		Collection<JFCEventId> ignoredEvents = new HashSet<JFCEventId>();
+		ignoredEvents.add(JFCEventId.FOCUS_GAINED);
+		JFCLogParser parser = new JFCLogParser(ignoredEvents);
+		parser.parseFile(new File(ClassLoader.getSystemResource("freemind_trace.xml").getFile()));
+		
+		Collection<List<Event>> sequences = parser.getSequences();
+		Event event = sequences.iterator().next().get(0);
+		
+		String result = new JFCReplayIDCalculator().calculateReplayID(event);
+		assertEquals("e3561778462", result);
+	}
+	
+	/**
+	 * Run the String calculateReplayID(List<JFCGUIElementSpec>) method test.
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void testCalculateReplayIDwithGuiElementPath()
+		throws Exception {
+		Collection<JFCEventId> ignoredEvents = new HashSet<JFCEventId>();
+		ignoredEvents.add(JFCEventId.FOCUS_GAINED);
+		JFCLogParser parser = new JFCLogParser(ignoredEvents);
+		parser.parseFile(new File(ClassLoader.getSystemResource("freemind_trace.xml").getFile()));
+		
+		Collection<List<Event>> sequences = parser.getSequences();
+		Event event = sequences.iterator().next().get(0);
+		
+		List<JFCGUIElementSpec> guiElementPath = new ArrayList<JFCGUIElementSpec>();
+		
+		IEventTarget target = event.getTarget();
+		JFCGUIElement jfcTarget = (JFCGUIElement) target;
+		
+		// extract element path
+		JFCGUIElement currentTarget = jfcTarget;
+		while (currentTarget != null){
+			JFCGUIElementSpec currentSpec = (JFCGUIElementSpec) currentTarget.getSpecification();
+			guiElementPath.add(0, currentSpec);
+			currentTarget = (JFCGUIElement) currentTarget.getParent();
+		}
+		
+		String result = new JFCReplayIDCalculator().calculateReplayID(guiElementPath);
+		assertEquals("e3561778462", result);
+	}
+	
+	/**
+	 * Method to test if calculated IDs are included in guitar efg file.
+	 * If not more than 75 % can be matched, test fails. 
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void testCalculateReplayIDAllEvents()
+		throws Exception {
+		// generate list of known replayIDs from guitar efg file
+		File guiFile = new File(ClassLoader.getSystemResource("freemind.xml").getFile());
+		JFCReplayIDValidator validator = new JFCReplayIDValidator(guiFile);
+		
+		// calculate replayIDs from trace file
+		Collection<JFCEventId> ignoredEvents = new HashSet<JFCEventId>();
+		ignoredEvents.add(JFCEventId.FOCUS_GAINED);
+		JFCLogParser parser = new JFCLogParser(ignoredEvents);
+		parser.parseFile(new File(ClassLoader.getSystemResource("freemind_trace3_corrected.xml").getFile()));
+		JFCReplayIDCalculator calculator = new JFCReplayIDCalculator(validator);
+		
+		Set<String> generatedIDs = new HashSet<String>(); 
+		
+		Collection<List<Event>> sequences = parser.getSequences();
+		
+		assertTrue(sequences.size() > 0);
+		
+		Set<Event> seenEvents = new HashSet<Event>();
+		
+		for (List<Event> currentSequence: sequences){
+			seenEvents.addAll(currentSequence);
+		}
+		
+		for (Event currentEvent: seenEvents){
+			String replayID = calculator.calculateReplayID(currentEvent);
+			generatedIDs.add(replayID);
+			System.out.println("Generated ID: " + replayID);
+			System.out.println();
+		}
+		
+		System.out.println();
+		
+		// check if generatedIDs are known
+		int known = 0;
+		for (String replayID: generatedIDs){
+			if (validator.validateReplayID("w" + replayID.substring(1))){
+				System.out.println(replayID + "\t is known.");
+				known++;
+			}
+			else
+				System.out.println(replayID + "\t is unknown.");
+		}
+		System.out.println();
+		
+		float percentage = (float) known/generatedIDs.size()*100;
+		System.out.println(percentage + "% of the generated IDs are known.");
+		
+		assertTrue(percentage > 75);	
+	}
+	
+	/**
+	 * Method to test if calculated IDs are included in guitar efg file.
+	 * If not more than 75 % can be matched, test fails.  
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void testCalculateReplayIDAllEventsArgo()
+		throws Exception {
+		// generate list of known replayIDs from guitar efg file
+		File guiFile = new File(ClassLoader.getSystemResource("argo.xml").getFile());
+		JFCReplayIDValidator validator = new JFCReplayIDValidator(guiFile);
+		
+		// calculate replayIDs from trace file
+		Collection<JFCEventId> ignoredEvents = new HashSet<JFCEventId>();
+		ignoredEvents.add(JFCEventId.FOCUS_GAINED);
+		JFCLogParser parser = new JFCLogParser(ignoredEvents);
+		parser.parseFile(new File(ClassLoader.getSystemResource("argouml_trace1_corrected.xml").getFile()));
+		JFCReplayIDCalculator calculator = new JFCReplayIDCalculator(validator);
+		
+		Set<String> generatedIDs = new HashSet<String>(); 
+		
+		Collection<List<Event>> sequences = parser.getSequences();
+		
+		assertTrue(sequences.size() > 0);
+		
+		Set<Event> seenEvents = new HashSet<Event>();
+		
+		for (List<Event> currentSequence: sequences){
+			seenEvents.addAll(currentSequence);
+		}
+		
+		for (Event currentEvent: seenEvents){
+			String replayID = calculator.calculateReplayID(currentEvent);
+			generatedIDs.add(replayID);
+			System.out.println("Generated ID: " + replayID);
+			System.out.println();
+		}
+		
+		System.out.println();
+		
+		// check if generatedIDs are known
+		int known = 0;
+		for (String replayID: generatedIDs){
+			if (validator.validateReplayID("w" + replayID.substring(1))){
+				System.out.println(replayID + "\t is known.");
+				known++;
+			}
+			else
+				System.out.println(replayID + "\t is unknown.");
+		}
+		System.out.println();
+		
+		float percentage = (float) known/generatedIDs.size()*100;
+		System.out.println(percentage + "% of the generated IDs are known.");
+		
+		assertTrue(percentage > 75);	
+	}
+	
+	
+	/**
+	 * Method to test if calculateReplayID throws the right exception when
+	 * it is called with a target of the wrong type.
+	 */
+	@Test
+	public void testCalculateReplayIDIllegalArgumentException(){
+		try{
+			JFCReplayIDCalculator calculator = new JFCReplayIDCalculator();
+			Event event = new Event(mock(IEventType.class), mock(IEventTarget.class));
+			
+			calculator.calculateReplayID(event);
+		
+			fail("Expected IllegalArgumentException!");
+		}
+		catch(IllegalArgumentException e){
+			System.out.println("Expected exception thrown.");
+		}
+	}
+
+	/**
+	 * Perform pre-test initialization.
+	 *
+	 * @throws Exception
+	 *         if the initialization fails for some reason
+	 *
+	 * @generatedBy CodePro at 7/30/12 4:58 PM
+	 */
+	@Before
+	public void setUp()
+		throws Exception {
+		    new TextConsole(Level.FINEST);
+	}
+
+	/**
+	 * Perform post-test clean-up.
+	 *
+	 * @throws Exception
+	 *         if the clean-up fails for some reason
+	 *
+	 * @generatedBy CodePro at 7/30/12 4:58 PM
+	 */
+	@After
+	public void tearDown()
+		throws Exception {
+		// Add additional tear down code here
+	}
+
+	/**
+	 * Launch the test.
+	 *
+	 * @param args the command line arguments
+	 *
+	 * @generatedBy CodePro at 7/30/12 4:58 PM
+	 */
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(JFCReplayIDCalculatorTest.class);
+	}
+}
+
+/*$CPS$ This comment was generated by CodePro. Do not edit it.
+ * patternId = com.instantiations.assist.eclipse.pattern.testCasePattern
+ * strategyId = com.instantiations.assist.eclipse.pattern.testCasePattern.junitTestCase
+ * additionalTestNames = 
+ * assertTrue = false
+ * callTestMethod = true
+ * createMain = false
+ * createSetUp = false
+ * createTearDown = false
+ * createTestFixture = false
+ * createTestStubs = false
+ * methods = 
+ * package = de.ugoe.cs.eventbench.efg
+ * package.sourceFolder = EventBenchConsoleTest/src
+ * superclassType = junit.framework.TestCase
+ * testCase = EFGEventIDCalculatorTest
+ * testClassType = de.ugoe.cs.eventbench.efg.EFGEventIDCalculator
+ */
Index: trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCTraceCorrectorTest.java
===================================================================
--- trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCTraceCorrectorTest.java	(revision 922)
+++ trunk/autoquest-plugin-jfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/jfc/JFCTraceCorrectorTest.java	(revision 922)
@@ -0,0 +1,96 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.logging.Level;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.plugin.jfc.JFCTraceCorrector;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: $
+ * @author 2011, last modified by $Author: $
+ */
+public class JFCTraceCorrectorTest {
+
+    /** */
+    private File outputFile = new File("tmp_output.xml");
+    
+    /**
+     *
+     */
+    @Before
+    public void setUp() {
+        new TextConsole(Level.FINEST);
+    }
+
+    /**
+     *
+     */
+    @After
+    public void tearDown() {
+        if ((outputFile != null) && (outputFile.exists())) {
+            outputFile.delete();
+        }
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void test() throws Exception {
+        JFCTraceCorrector corrector = new JFCTraceCorrector();
+        corrector.correctFile(getTestFile("uncorrected_trace.xml"), outputFile);
+        
+        BufferedReader reader1 = null;
+        BufferedReader reader2 = null;
+
+        try {
+            reader1 = new BufferedReader(new FileReader(getTestFile("corrected_trace.xml")));
+            reader2 = new BufferedReader(new FileReader(outputFile));
+            
+            String line;
+            do {
+                line = reader1.readLine();
+                if (line != null) {
+                    assertEquals(line, reader2.readLine());
+                }
+                else {
+                    assertNull(reader2.readLine());
+                }
+            }
+            while (line != null);
+        }
+        finally {
+            if (reader1 != null) {
+                reader1.close();
+            }
+            if (reader2 != null) {
+                reader2.close();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param string
+     * @return
+     */
+    private File getTestFile(String name) {
+        return new File(ClassLoader.getSystemResource(name).getFile());
+    }
+
+}
Index: trunk/autoquest-plugin-jfc-test/src/test/resources/GUIElementMapping.txt
===================================================================
--- trunk/autoquest-plugin-jfc-test/src/test/resources/GUIElementMapping.txt	(revision 921)
+++ trunk/autoquest-plugin-jfc-test/src/test/resources/GUIElementMapping.txt	(revision 922)
@@ -1,18 +1,18 @@
-org.argouml.ui.ProjectBrowser = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCFrame
-org.tigris.toolbar.ToolBar = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCToolBar
-org.tigris.toolbar.toolbutton.ModalButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCButton
-org.tigris.swidgets.BorderSplitPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCSplitPane
-org.tigris.swidgets.MultipleSplitPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCSplitPane
-org.argouml.ui.MultiEditorPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.uml.diagram.ui.TabDiagram = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.uml.diagram.ui.DnDJGraph = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCCanvas
-org.tigris.gef.graph.presentation.JGraphInternalPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCCanvas
-org.tigris.toolbar.toolbutton.PopupToolBoxButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenuButton
-org.argouml.ui.DetailsPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.cognitive.ui.TabToDo = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.tigris.swidgets.Splitter = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCSplitPane
-org.argouml.ui.MenuBar14 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenuBar
-org.tigris.gef.presentation.FigTextEditor = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-org.argouml.core.propertypanels.ui.UMLLinkedList = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCShape
-org.argouml.core.propertypanels.ui.LabelledComponent = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCShape
-org.argouml.core.propertypanels.ui.UMLTextField = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
+org.argouml.ui.ProjectBrowser = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCFrame
+org.tigris.toolbar.ToolBar = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCToolBar
+org.tigris.toolbar.toolbutton.ModalButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCButton
+org.tigris.swidgets.BorderSplitPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCSplitPane
+org.tigris.swidgets.MultipleSplitPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCSplitPane
+org.argouml.ui.MultiEditorPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.uml.diagram.ui.TabDiagram = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.uml.diagram.ui.DnDJGraph = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCCanvas
+org.tigris.gef.graph.presentation.JGraphInternalPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCCanvas
+org.tigris.toolbar.toolbutton.PopupToolBoxButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenuButton
+org.argouml.ui.DetailsPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.cognitive.ui.TabToDo = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.tigris.swidgets.Splitter = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCSplitPane
+org.argouml.ui.MenuBar14 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenuBar
+org.tigris.gef.presentation.FigTextEditor = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+org.argouml.core.propertypanels.ui.UMLLinkedList = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCShape
+org.argouml.core.propertypanels.ui.LabelledComponent = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCShape
+org.argouml.core.propertypanels.ui.UMLTextField = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
Index: trunk/autoquest-plugin-jfc/data/guimappings/guimapping-argouml.txt
===================================================================
--- trunk/autoquest-plugin-jfc/data/guimappings/guimapping-argouml.txt	(revision 921)
+++ trunk/autoquest-plugin-jfc/data/guimappings/guimapping-argouml.txt	(revision 922)
@@ -1,43 +1,43 @@
-org.argouml.cognitive.checklist.ui.TabChecklist = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.cognitive.ui.TabToDo = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.cognitive.ui.WizDescription = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.core.propertypanels.ui.LabelledComponent = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.core.propertypanels.ui.OldScrollList = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCScrollPane
-org.argouml.core.propertypanels.ui.ScrollListImpl = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCScrollPane
-org.argouml.core.propertypanels.ui.UMLComboBox = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCComboBox
-org.argouml.core.propertypanels.ui.UMLEditableComboBox$UMLComboBoxEditor$UMLImagePanel = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.core.propertypanels.ui.UMLExpressionBodyField = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-org.argouml.core.propertypanels.ui.UMLExpressionLanguageField = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-org.argouml.core.propertypanels.ui.UMLLinkedList = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCListBox
-org.argouml.core.propertypanels.ui.UMLMultiplicityPanel$MultiplicityCheckBox = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCCheckBox
-org.argouml.core.propertypanels.ui.UMLMutableLinkedList = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCListBox
-org.argouml.core.propertypanels.ui.UMLTextField = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-org.argouml.ui.ArgoJMenu = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenu
-org.argouml.ui.DetailsPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.ui.HelpBox = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCDialog
-org.argouml.ui.MenuBar14 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenuBar
-org.argouml.ui.MultiEditorPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.ui.NavigatorPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.ui.ProjectBrowser = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCFrame
-org.argouml.ui.SettingsDialog = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCDialog
-org.argouml.ui.explorer.DnDExplorerTree = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTree
-org.argouml.ui.explorer.PerspectiveComboBox = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCComboBox
-org.argouml.uml.diagram.ui.DnDJGraph = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCCanvas
-org.argouml.uml.diagram.ui.TabDiagram = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.uml.ui.TabDocumentation = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.uml.ui.TabStereotype = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.uml.ui.TabTaggedValues = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.uml.ui.UMLComboBox2 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCComboBox
-org.argouml.uml.ui.UMLEditableComboBox$UMLComboBoxEditor$UMLImagePanel = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-org.argouml.uml.ui.UMLLinkedList = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCListBox
-org.argouml.uml.ui.UMLTextArea2 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextArea
-org.tigris.gef.graph.presentation.JGraphInternalPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCCanvas
-org.tigris.gef.presentation.FigTextEditor = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-org.tigris.swidgets.ArrowButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCButton
-org.tigris.swidgets.BorderSplitPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCSplitPane
-org.tigris.swidgets.MultipleSplitPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCSplitPane
-org.tigris.swidgets.Splitter = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCSplitPane
-org.tigris.toolbar.ToolBar = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCToolBar
-org.tigris.toolbar.toolbutton.ModalButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCButton
-org.tigris.toolbar.toolbutton.PopupToolBoxButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenuButton
-org.argouml.core.propertypanels.ui.UMLExpressionLanguageField = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
+org.argouml.cognitive.checklist.ui.TabChecklist = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.cognitive.ui.TabToDo = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.cognitive.ui.WizDescription = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.core.propertypanels.ui.LabelledComponent = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.core.propertypanels.ui.OldScrollList = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCScrollPane
+org.argouml.core.propertypanels.ui.ScrollListImpl = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCScrollPane
+org.argouml.core.propertypanels.ui.UMLComboBox = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCComboBox
+org.argouml.core.propertypanels.ui.UMLEditableComboBox$UMLComboBoxEditor$UMLImagePanel = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.core.propertypanels.ui.UMLExpressionBodyField = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+org.argouml.core.propertypanels.ui.UMLExpressionLanguageField = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+org.argouml.core.propertypanels.ui.UMLLinkedList = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCListBox
+org.argouml.core.propertypanels.ui.UMLMultiplicityPanel$MultiplicityCheckBox = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCCheckBox
+org.argouml.core.propertypanels.ui.UMLMutableLinkedList = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCListBox
+org.argouml.core.propertypanels.ui.UMLTextField = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+org.argouml.ui.ArgoJMenu = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu
+org.argouml.ui.DetailsPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.ui.HelpBox = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCDialog
+org.argouml.ui.MenuBar14 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenuBar
+org.argouml.ui.MultiEditorPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.ui.NavigatorPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.ui.ProjectBrowser = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCFrame
+org.argouml.ui.SettingsDialog = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCDialog
+org.argouml.ui.explorer.DnDExplorerTree = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTree
+org.argouml.ui.explorer.PerspectiveComboBox = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCComboBox
+org.argouml.uml.diagram.ui.DnDJGraph = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCCanvas
+org.argouml.uml.diagram.ui.TabDiagram = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.uml.ui.TabDocumentation = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.uml.ui.TabStereotype = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.uml.ui.TabTaggedValues = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.uml.ui.UMLComboBox2 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCComboBox
+org.argouml.uml.ui.UMLEditableComboBox$UMLComboBoxEditor$UMLImagePanel = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+org.argouml.uml.ui.UMLLinkedList = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCListBox
+org.argouml.uml.ui.UMLTextArea2 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextArea
+org.tigris.gef.graph.presentation.JGraphInternalPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCCanvas
+org.tigris.gef.presentation.FigTextEditor = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+org.tigris.swidgets.ArrowButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCButton
+org.tigris.swidgets.BorderSplitPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCSplitPane
+org.tigris.swidgets.MultipleSplitPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCSplitPane
+org.tigris.swidgets.Splitter = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCSplitPane
+org.tigris.toolbar.ToolBar = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCToolBar
+org.tigris.toolbar.toolbutton.ModalButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCButton
+org.tigris.toolbar.toolbutton.PopupToolBoxButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenuButton
+org.argouml.core.propertypanels.ui.UMLExpressionLanguageField = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
Index: trunk/autoquest-plugin-jfc/data/guimappings/guimapping-freemind.txt
===================================================================
--- trunk/autoquest-plugin-jfc/data/guimappings/guimapping-freemind.txt	(revision 921)
+++ trunk/autoquest-plugin-jfc/data/guimappings/guimapping-freemind.txt	(revision 922)
@@ -1,10 +1,10 @@
-freemind.main.FreeMind = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCFrame
-freemind.controller.MainToolBar = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCToolBar
-freemind.modes.mindmapmode.MindMapToolBar = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCToolBar
-freemind.view.mindmapview.MapView$ScrollPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCScrollPane
-freemind.view.mindmapview.MapView = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCCanvas
-freemind.view.mindmapview.NodeView = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCShape
-freemind.view.mindmapview.RootMainView = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCShape
-freemind.controller.MenuBar = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenuBar
-freemind.view.mindmapview.ForkMainView = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCShape
-freemind.modes.FreeMindJFileDialog = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCDialog
+freemind.main.FreeMind = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCFrame
+freemind.controller.MainToolBar = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCToolBar
+freemind.modes.mindmapmode.MindMapToolBar = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCToolBar
+freemind.view.mindmapview.MapView$ScrollPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCScrollPane
+freemind.view.mindmapview.MapView = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCCanvas
+freemind.view.mindmapview.NodeView = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCShape
+freemind.view.mindmapview.RootMainView = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCShape
+freemind.controller.MenuBar = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenuBar
+freemind.view.mindmapview.ForkMainView = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCShape
+freemind.modes.FreeMindJFileDialog = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCDialog
Index: trunk/autoquest-plugin-jfc/data/guimappings/guimapping-swing.txt
===================================================================
--- trunk/autoquest-plugin-jfc/data/guimappings/guimapping-swing.txt	(revision 921)
+++ trunk/autoquest-plugin-jfc/data/guimappings/guimapping-swing.txt	(revision 922)
@@ -1,34 +1,34 @@
-javax.swing.JButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCButton
-javax.swing.JComboBox = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCComboBox
-javax.swing.JDialog = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCDialog
-javax.swing.JEditorPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextArea
-javax.swing.JFileChooser = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCDialog
-javax.swing.JLayeredPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-javax.swing.JMenu = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenu
-javax.swing.JMenu$1 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenu
-javax.swing.JMenuItem = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenu
-javax.swing.JOptionPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCDialog
-javax.swing.JPanel = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-javax.swing.JPopupMenu = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenu
-javax.swing.JPopupMenu$1 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenu
-javax.swing.JRadioButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCRadioButton
-javax.swing.JRootPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-javax.swing.JScrollPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCScrollPane
-javax.swing.JScrollPane$ScrollBar = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCScrollBar
-javax.swing.JSplitPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCSplitPane
-javax.swing.JTabbedPane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTabbedPane
-javax.swing.JTable = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTable
-javax.swing.JTextArea = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextArea
-javax.swing.JTextField = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-javax.swing.JToolBar$1 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCToolBar
-javax.swing.JViewport = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-javax.swing.Popup$HeavyWeightWindow = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCDialog
-javax.swing.plaf.basic.BasicComboBoxEditor$BorderlessTextField = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-javax.swing.plaf.basic.BasicComboPopup$1 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCMenu
-javax.swing.plaf.metal.MetalComboBoxButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCButton
-javax.swing.plaf.metal.MetalFileChooserUI$1 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-javax.swing.plaf.metal.MetalFileChooserUI$3 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCTextField
-javax.swing.plaf.metal.MetalScrollButton = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCScrollBar
-sun.swing.FilePane = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-sun.swing.FilePane$3 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
-sun.swing.FilePane$4 = de.ugoe.cs.quest.plugin.jfc.guimodel.JFCPanel
+javax.swing.JButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCButton
+javax.swing.JComboBox = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCComboBox
+javax.swing.JDialog = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCDialog
+javax.swing.JEditorPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextArea
+javax.swing.JFileChooser = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCDialog
+javax.swing.JLayeredPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+javax.swing.JMenu = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu
+javax.swing.JMenu$1 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu
+javax.swing.JMenuItem = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu
+javax.swing.JOptionPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCDialog
+javax.swing.JPanel = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+javax.swing.JPopupMenu = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu
+javax.swing.JPopupMenu$1 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu
+javax.swing.JRadioButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCRadioButton
+javax.swing.JRootPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+javax.swing.JScrollPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCScrollPane
+javax.swing.JScrollPane$ScrollBar = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCScrollBar
+javax.swing.JSplitPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCSplitPane
+javax.swing.JTabbedPane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTabbedPane
+javax.swing.JTable = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTable
+javax.swing.JTextArea = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextArea
+javax.swing.JTextField = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+javax.swing.JToolBar$1 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCToolBar
+javax.swing.JViewport = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+javax.swing.Popup$HeavyWeightWindow = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCDialog
+javax.swing.plaf.basic.BasicComboBoxEditor$BorderlessTextField = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+javax.swing.plaf.basic.BasicComboPopup$1 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu
+javax.swing.plaf.metal.MetalComboBoxButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCButton
+javax.swing.plaf.metal.MetalFileChooserUI$1 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+javax.swing.plaf.metal.MetalFileChooserUI$3 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCTextField
+javax.swing.plaf.metal.MetalScrollButton = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCScrollBar
+sun.swing.FilePane = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+sun.swing.FilePane$3 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
+sun.swing.FilePane$4 = de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCPanel
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCLogParser.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCLogParser.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCLogParser.java	(revision 922)
@@ -0,0 +1,704 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonUp;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementFactory;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
+import de.ugoe.cs.autoquest.plugin.jfc.eventcore.JFCEventId;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElementSpec;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * This class provides functionality to parse XML log files generated by the JFCMonitor of
+ * EventBench. The result of parsing a file is a collection of event sequences.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class JFCLogParser extends DefaultHandler {
+
+    /**
+     * <p>
+     * Collection of event sequences that is contained in the log file, which is parsed.
+     * </p>
+     */
+    private Collection<List<Event>> sequences;
+
+    /**
+     * <p>
+     * Internal handle to the id of the event that is currently being parsed.
+     * </p>
+     */
+    private JFCEventId currentEventId;
+    
+    /**
+     * <p>
+     * Internal handle to the parameters of the event that is currently being parsed.
+     * </p>
+     */
+    private Map<String, String> currentEventParameters;
+
+    /**
+     * <p>
+     * Internal handle to the source parameters of the event that is currently being parsed.
+     * </p>
+     */
+    private Map<String, String> currentSourceParameters;
+
+    /**
+     * <p>
+     * Internal handle to the event sequence that is currently being parsed.
+     * </p>
+     */
+    private List<Event> currentSequence;
+   
+    /**
+     * <p>
+     * internal handle to the parameters currently parsed for a component
+     * </p>
+     */
+    private JFCGUIElementSpec currentGuiElementSpec;
+
+    /**
+     * <p>
+     * internal handle to the last parsed component
+     * </p>
+     */
+    private List<JFCGUIElementSpec> currentGuiElementPath = new ArrayList<JFCGUIElementSpec>();
+
+    /**
+     * <p>
+     * internal handle to the component of the previous event to be potentially reused for the
+     * current
+     * </p>
+     */
+    private IGUIElement lastGUIElement;
+
+    /**
+     * <p>
+     * the model of the GUI elements, that were found during parsing
+     * </p>
+     */
+    private GUIModel guiModel = new GUIModel();
+
+    /**
+     * <p>
+     * this is used to check, if for every pressed key, there is a release of it
+     * </p>
+     */
+    private List<VirtualKey> mPressedKeys = new ArrayList<VirtualKey>();
+
+    /**
+     * <p>
+     * Enumeration to differentiate if a parameter belongs to an event, a source or the parent of a
+     * source.
+     * </p>
+     * 
+     * @author Steffen Herbold
+     * @version 1.0
+     */
+    private enum ParamSource {
+        EVENT, SOURCE, PARENT, COMPONENT
+    };
+
+    /**
+     * <p>
+     * Specifies whether the parameters that are currently being read belong the the event, the
+     * source or the parent.
+     * </p>
+     */
+    private ParamSource paramSource = null;
+
+    /**
+     * <p>
+     * a specification for event ids to be omitted by the parser
+     * </p>
+     */
+    private Collection<JFCEventId> eventFilter;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCLogParser with a default event filter. This ignores focus
+     * events, mouse pressed, and mouse released events.
+     * </p>
+     */
+    public JFCLogParser() {
+        sequences = new LinkedList<List<Event>>();
+        currentSequence = null;
+        //setupDefaultEventFilter();
+    }
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCLogParser with a specific event filter. The events in the
+     * provided collection are ignored by the parser. As events, the constants of the different
+     * event classes must be used. E.g. creating a collection and putting
+     * <code>MouseEvent.MOUSE_PRESSED</code> will cause the parser to ignore all mouse pressed
+     * events. If the provided collection is null, no event is ignored.
+     * </p>
+     * 
+     * @param ignoredEvents
+     *            the events to be ignored by the parser, can be null
+     */
+    public JFCLogParser(Collection<JFCEventId> ignoredEvents) {
+        sequences = new LinkedList<List<Event>>();
+        currentSequence = null;
+        eventFilter = ignoredEvents;
+    }
+
+    /**
+     * <p>
+     * Parses a log file written by the JFCMonitor and creates a collection of event sequences.
+     * </p>
+     * 
+     * @param filename
+     *            name and path of the log file
+     */
+    public void parseFile(String filename) {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename must not be null");
+        }
+
+        parseFile(new File(filename));
+    }
+
+    /**
+     * <p>
+     * Parses a log file written by the JFCMonitor and creates a collection of event sequences.
+     * </p>
+     * 
+     * @param file
+     *            name and path of the log file
+     */
+    public void parseFile(File file) {
+        if (file == null) {
+            throw new IllegalArgumentException("file must not be null");
+        }
+
+        SAXParserFactory spf = SAXParserFactory.newInstance();
+        spf.setValidating(true);
+
+        SAXParser saxParser = null;
+        InputSource inputSource = null;
+        try {
+            saxParser = spf.newSAXParser();
+            inputSource =
+                new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8"));
+        }
+        catch (UnsupportedEncodingException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        catch (ParserConfigurationException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        catch (SAXException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        catch (FileNotFoundException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return;
+        }
+        if (inputSource != null) {
+            inputSource.setSystemId("file://" + file.getAbsolutePath());
+            try {
+                if (saxParser == null) {
+                    throw new RuntimeException("SAXParser creation failed");
+                }
+                saxParser.parse(inputSource, this);
+            }
+            catch (SAXParseException e) {
+                Console.printerrln("Failure parsing file in line " + e.getLineNumber() +
+                    ", column " + e.getColumnNumber() + ".");
+                Console.logException(e);
+                return;
+            }
+            catch (SAXException e) {
+                Console.printerr("Error parsing file + " + file.getName());
+                Console.logException(e);
+                return;
+            }
+            catch (IOException e) {
+                Console.printerr("Error parsing file + " + file.getName());
+                Console.logException(e);
+                return;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the collection of event sequences that is obtained from parsing log files.
+     * </p>
+     * 
+     * @return collection of event sequences
+     */
+    public Collection<List<Event>> getSequences() {
+        return sequences;
+    }
+
+    /**
+     * <p>
+     * Returns the gui model that is obtained from parsing log files.
+     * </p>
+     * 
+     * @return collection of event sequences
+     */
+    public GUIModel getGuiModel() {
+        return guiModel;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
+     * java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String uri, String localName, String qName, Attributes atts)
+        throws SAXException
+    {
+        if (qName.equals("sessions")) {
+            currentSequence = new LinkedList<Event>();
+        }
+        if (qName.equals("newsession")) {
+            Console.traceln(Level.FINE, "start of session");
+            if (currentSequence != null && !currentSequence.isEmpty()) {
+                // create a copy of the list just to have a correctly typed one.
+                sequences.add(currentSequence.subList(0, currentSequence.size() - 1));
+            }
+            currentSequence = new LinkedList<Event>();
+        }
+        else if (qName.equals("event")) {
+            JFCEventId eventId = JFCEventId.parseEventId(atts.getValue("id"));
+            if ((eventFilter == null) || (!eventFilter.contains(eventId))) {
+                currentEventId = eventId;
+                currentEventParameters = new HashMap<String, String>();
+                currentSourceParameters = new HashMap<String, String>();
+                paramSource = ParamSource.EVENT;
+            }
+        }
+        else if (currentEventId != null) {
+            if (qName.equals("param")) {
+                if (paramSource == ParamSource.EVENT) {
+                    currentEventParameters.put(atts.getValue("name"), atts.getValue("value"));
+                }
+                else if (paramSource == ParamSource.SOURCE) {
+                    currentSourceParameters.put(atts.getValue("name"), atts.getValue("value"));
+                }
+                else if (paramSource == ParamSource.COMPONENT) {
+                    if ("title".equals(atts.getValue("name"))) {
+                        currentGuiElementSpec.setName(atts.getValue("value"));
+                    }
+                    else if ("class".equals(atts.getValue("name"))) {
+                        currentGuiElementSpec.setType(atts.getValue("value"));
+                    }
+                    else if ("icon".equals(atts.getValue("name"))) {
+                        currentGuiElementSpec.setIcon(atts.getValue("value"));
+                    }
+                    else if ("index".equals(atts.getValue("name"))) {
+                        currentGuiElementSpec.setIndex(Integer.parseInt(atts.getValue("value")));
+                    }
+                    else if ("hash".equals(atts.getValue("name"))) {
+                        currentGuiElementSpec.setElementHash
+                            ((int) Long.parseLong(atts.getValue("value"), 16));
+                    }
+                }
+            }
+            else if (qName.equals("source")) {
+                paramSource = ParamSource.SOURCE;
+            }
+            else if (qName.equals("parent")) {
+                paramSource = ParamSource.PARENT;
+            }
+            else if (qName.equals("component")) {
+                paramSource = ParamSource.COMPONENT;
+                currentGuiElementSpec = new JFCGUIElementSpec();
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
+     * java.lang.String)
+     */
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (qName.equals("sessions")) {
+            if (currentSequence != null && !currentSequence.isEmpty()) {
+                sequences.add(currentSequence);
+            }
+            currentSequence = null;
+        }
+        else if (currentEventId != null) {
+            if (qName.equals("event")) {
+                
+                if (currentGuiElementPath.size() <= 0)
+                {
+                    // no component specification available. Try to parse the GUI element from
+                    // the toString parameter
+                    currentGuiElementSpec = new JFCGUIElementSpec();
+                    getGUIElementSpecFromToString(currentSourceParameters.get("toString"));
+                    currentGuiElementPath.add(currentGuiElementSpec);
+                    currentGuiElementSpec = null;
+                }
+                
+                IGUIElement currentGUIElement;
+                try {
+                    currentGUIElement = guiModel.integratePath
+                        (currentGuiElementPath, GUIElementFactory.getInstance());
+                }
+                catch (GUIModelException e) {
+                    throw new SAXException("error in the GUI model provided in the log", e);
+                }
+                
+                Event event = new Event
+                    (instantiateInteraction(currentEventId, currentEventParameters),
+                     (currentGUIElement == null ? lastGUIElement : currentGUIElement));
+                
+                currentSequence.add(event);
+                
+                currentEventParameters = null;
+                currentSourceParameters = null;
+                currentGuiElementSpec = null;
+                currentGuiElementPath.clear();
+                
+                currentEventId = null;
+                
+                if (currentGUIElement != null) {
+                    lastGUIElement = currentGUIElement;
+                }
+                
+                currentGUIElement = null;
+            }
+            else if (qName.equals("source")) {
+                paramSource = ParamSource.EVENT;
+            }
+            else if (qName.equals("parent")) {
+                paramSource = ParamSource.SOURCE;
+            }
+            else if (qName.equals("component")) {
+                paramSource.equals(ParamSource.SOURCE);
+                currentGuiElementPath.add(currentGuiElementSpec);
+                currentGuiElementSpec = null;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * depending on the event id and the event parameters, this method instantiates the concrete
+     * interaction, that took place, i.e. the event type
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id is unknown
+     */
+    private IInteraction instantiateInteraction(JFCEventId          eventId,
+                                                Map<String, String> eventParameters)
+      throws SAXException
+    {
+        switch (eventId)
+        {
+            case FOCUS_GAINED:
+                return handleNewFocus(eventId, eventParameters);
+
+            case KEY_PRESSED:
+            case KEY_RELEASED:
+            case KEY_TYPED:
+                return handleKeyAction(eventId, eventParameters);
+
+            case MOUSE_CLICKED:
+            case MOUSE_PRESSED:
+            case MOUSE_RELEASED:
+            case MOUSE_MOVED:
+            case MOUSE_ENTERED:
+            case MOUSE_EXITED:
+            case MOUSE_DRAGGED:
+            case MOUSE_WHEEL:
+                return handleMouseAction(eventId, eventParameters);
+
+            default:
+                throw new SAXException("unhandled event id " + eventId);
+        }
+    }
+
+    /**
+     * <p>
+     * handles a mouse interaction. The method determines based on the event id and the parameters
+     * which mouse button is pressed, released or clicked.
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id or button index is unknown
+     */
+    private IInteraction handleMouseAction(JFCEventId eventId, Map<String, String> eventParameters)
+        throws SAXException
+    {
+        MouseButtonInteraction.Button button = null;
+
+        if (eventParameters.get("Button") != null)
+        {
+            int buttonId = Integer.parseInt(eventParameters.get("Button"));
+            if (buttonId == MouseEvent.BUTTON1)
+            {
+                button = MouseButtonInteraction.Button.LEFT;
+            }
+            else if (buttonId == MouseEvent.BUTTON2)
+            {
+                button = MouseButtonInteraction.Button.MIDDLE;
+            }
+            else if (buttonId == MouseEvent.BUTTON3)
+            {
+                button = MouseButtonInteraction.Button.RIGHT;
+            }
+            else
+            {
+                throw new SAXException("unknown mouse button index " + buttonId);
+            }
+        }
+
+        if (JFCEventId.MOUSE_CLICKED == eventId)
+        {
+            return new MouseClick(button);
+        }
+        else if (JFCEventId.MOUSE_PRESSED == eventId)
+        {
+            return new MouseButtonDown(button);
+        }
+        else if (JFCEventId.MOUSE_RELEASED == eventId)
+        {
+            return new MouseButtonUp(button);
+        }
+        else
+        {
+            throw new SAXException("unknown event id " + eventId);
+        }
+    }
+
+    /**
+     * <p>
+     * handles a keyboard interaction. The method determines based on the event id and the
+     * parameters which key on the keyboard is pressed or released. It further checks, if for 
+     * every released key there is also a pressed event
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id is unknown or if there is a key
+     *                      release without a preceding press of the same key
+     */
+    private IInteraction handleKeyAction(JFCEventId eventId, Map<String, String> eventParameters)
+      throws SAXException
+    {
+        // TODO handle shortcuts
+        if (JFCEventId.KEY_PRESSED == eventId)
+        {
+            VirtualKey key = VirtualKey.parseVirtualKey(eventParameters.get("KeyCode"));
+            mPressedKeys.add(key);
+
+            return new KeyPressed(key);
+        }
+        else if (JFCEventId.KEY_RELEASED == eventId)
+        {
+            VirtualKey key = VirtualKey.parseVirtualKey(eventParameters.get("KeyCode"));
+            if (mPressedKeys.contains(key))
+            {
+                mPressedKeys.remove(key);
+            }
+            else
+            {
+                Console.traceln(Level.SEVERE, "log file has an error, as it contains a key up event on key " +
+                                   key + " for which there is no preceeding key down event");
+            }
+
+            return new KeyReleased(key);
+        }
+
+        throw new SAXException("unknown event id " + eventId);
+    }
+
+    /**
+     * <p>
+     * handles explicit keyboard focus changes.
+     * </p>
+     *
+     * @param eventId
+     *            the id of the event
+     * @param eventParameters
+     *            the parameters provided for the event
+     *            
+     * @return as described
+     * 
+     * @throws SAXException thrown if the provided event id is unknown
+     */
+    private IInteraction handleNewFocus(JFCEventId eventId, Map<String, String> eventParameters)
+        throws SAXException
+    {
+        if (JFCEventId.FOCUS_GAINED == eventId)
+        {
+            return new KeyboardFocusChange();
+        }
+        else
+        {
+            throw new SAXException("unknown event id " + eventId);
+        }
+    }
+
+    /**
+     * <p>
+     * for some events in the log file, no component specification is provided. In this case the
+     * GUI element on which the event is executed must be determined based on the
+     * <code>toString</code> parameter of the event. This is achieved through this method. The
+     * <code>toString</code> parameter does not always carry sufficient information for the GUI
+     * elements. For example the title is not necessarily provided. Therefore some of this
+     * information is generated.
+     * </p>
+     *
+     * @param toStringValue
+     *            the <code>toString</code> parameter of the event to be parsed for the GUI element
+     *            
+     * @return the appropriate GUI Element
+     * 
+     * @throws SAXException thrown if the provided value of the <code>toString</code> parameter
+     *                      can not be parsed
+     */
+    private void getGUIElementSpecFromToString(String toStringValue)
+        throws SAXException
+    {
+        try
+        {
+            // match the following: <type>[<parameters>]
+            String pattern = "([\\w$\\.]*)\\[(.*)\\]";
+            Matcher matcher = Pattern.compile(pattern).matcher(toStringValue);
+
+            if (!matcher.find())
+            {
+                throw new IllegalArgumentException
+                    ("could not parse target from toString parameter");
+            }
+
+            String type = matcher.group(1);
+            
+            // match the following: <parameter>|,
+            // where <parameter> := <digitValue>|<value>|<key>"="<value>
+            pattern = "([\\w$@=\\.]*)|,";
+
+            matcher = Pattern.compile(pattern).matcher(matcher.group(2));
+            
+            float elementHash = -1;
+            
+            pattern = "(([\\d]*)|([\\w$]*)|(([\\w$@\\.]*)=([\\w$@\\.]*)))\\z";
+            Pattern valuePattern = Pattern.compile(pattern);
+            
+            while (matcher.find()) {
+                Matcher valueMatcher = valuePattern.matcher(matcher.group(1));
+                if (valueMatcher.find()) {
+                    if ((valueMatcher.group(2) != null) && (!"".equals(valueMatcher.group(2)))) {
+                        // found digit value. Those in combination usually denote the position
+                        // of the GUI element. So calculate an element has out of them
+                        elementHash += Integer.parseInt(valueMatcher.group(2));
+                    }
+                    else if ((valueMatcher.group(5) != null) &&
+                             (!"".equals(valueMatcher.group(5))) &&
+                             (valueMatcher.group(6) != null) &&
+                             (!"".equals(valueMatcher.group(6))))
+                    {
+                        // found a key value pair. Get some of them and integrate them into the hash 
+                        String key = valueMatcher.group(5);
+                        
+                        if ("alignmentX".equals(key) || "alignmentY".equals(key)) {
+                            elementHash += Float.parseFloat(valueMatcher.group(6));
+                        }
+                    }
+                }
+            }
+
+            currentGuiElementSpec.setName("unknown(" + ((int) elementHash) + ")");
+            currentGuiElementSpec.setType(type);
+            currentGuiElementSpec.setIndex(-1);
+            currentGuiElementSpec.setElementHash((int) elementHash);
+        }
+        catch (Exception e)
+        {
+            throw new SAXException("could not parse target", e);
+        }
+    }
+    
+    /**
+     * <p>
+     * creates a default event filter that ignores focus changes, mouse pressed and mouse released
+     * events.
+     * </p>
+     */
+    /*private void setupDefaultEventFilter() {
+        eventFilter = new HashSet<JFCEventId>();
+        eventFilter.add(JFCEventId.MOUSE_PRESSED);
+        eventFilter.add(JFCEventId.MOUSE_RELEASED);
+        eventFilter.add(JFCEventId.FOCUS_GAINED);
+    }*/
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCPlugin.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCPlugin.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCPlugin.java	(revision 922)
@@ -0,0 +1,47 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+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 JFC plug-in.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class JFCPlugin implements QuestPlugin {
+
+    /**
+     * <p>
+     * The command packages of this plug-in.
+     * </p>
+     */
+    private final static String[] commandPackages = new String[]
+        { "de.ugoe.cs.autoquest.plugin.jfc.commands" };
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.QuestPlugin#getTitle()
+     */
+    @Override
+    public String getTitle() {
+        return "JFC-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-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDCalculator.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDCalculator.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDCalculator.java	(revision 922)
@@ -0,0 +1,283 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
+import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElementSpec;
+
+
+/**
+ * <p>
+ * This class provides the functionality to calculate the unique GUITAR replayIDs
+ * for {@link JFCEvent}s. This code is mainly based on guitar source code: edu.umd.cs.guitar.
+ * model.JFCDefaultIDGeneratorSimple
+ *    
+ * </p>
+ * @author fabian.glaser
+ *
+ */
+
+public class JFCReplayIDCalculator {
+	
+	static final int prime = 31;
+	private JFCReplayIDValidator validator;
+	
+	public JFCReplayIDCalculator() {
+		this.validator = null;
+	}
+	
+	public JFCReplayIDCalculator(JFCReplayIDValidator validator){
+		this.validator = validator;
+	}
+	
+	/**
+	 * Properties that are used to identify widgets
+	 */
+	private static List<String> ID_PROPERTIES = Arrays.asList(
+			"Class","Title","Icon");
+	
+   /**
+    * Those classes are invisible widgets but cause false-positive when
+    * calculating ID (compare guitar source code: edu.umd.cs.guitar.
+    * model.JFCDefaultIDGeneratorSimple)
+    */
+   private static List<String> IGNORED_CLASSES = Arrays.asList("javax.swing.JPanel",
+         "javax.swing.JTabbedPane", "javax.swing.JScrollPane",
+         "javax.swing.JSplitPane", "javax.swing.Box",
+         "javax.swing.JViewport", "javax.swing.JScrollBar",
+         "javax.swing.JLayeredPane",
+         "javax.swing.JList$AccessibleJList$AccessibleJListChild",
+         "javax.swing.JList$AccessibleJList", "javax.swing.JList",
+         "javax.swing.JScrollPane$ScrollBar",
+         "javax.swing.plaf.metal.MetalScrollButton");
+   
+   /**
+    * Calculates the replayID of a JFCEvent needed for compatibility with guitar suite
+    * @param List of {@link JFCGUIElementSpec}s that represent the component path of a event target
+    * for which the replayID should be calculated. 
+    * @return replayID
+    */
+	
+   public String calculateReplayID(List<JFCGUIElementSpec> guiElementPath){
+	   String replayID = "";
+	   long hashCode = 1;
+	 
+	   ListIterator<JFCGUIElementSpec> iterator = guiElementPath.listIterator();
+	   
+	   JFCGUIElementSpec currentSpec = iterator.next();
+	   String title = currentSpec.getName();
+	   String fuzzyTitle = getFuzzyTitle(title);
+	   long windowHashCode = fuzzyTitle.hashCode();
+	   windowHashCode = (windowHashCode * 2) & 0xffffffffL;
+
+	   long propagatedHashCode = windowHashCode;
+	   
+	   // added validator to check if generated component ids are known
+	   if (validator != null){
+		   if (validator.validateReplayID("w" + windowHashCode)){
+			   System.out.println("ID w" + windowHashCode + " is valid.");
+		   }
+		   else{
+			   System.err.println(currentSpec + " describes an unknown component.");
+		   	   System.err.println("ID w" + windowHashCode + " is unknown." );
+		   	   System.err.println();
+		   }
+			   
+	   }
+	   
+	   // construct looks complicated but avoids going back and forth through path
+	   if (iterator.hasNext())
+		   currentSpec = iterator.next();
+	   else{
+		   currentSpec = null;
+		   // there are no subcomponents, so we use windowHashCode as hashCode
+		   hashCode = windowHashCode;
+	   }
+
+	   // walk through component path and calculate hashcode
+	   while(currentSpec != null){
+		   long localHashCode = getLocalHashCode(currentSpec);
+		   hashCode = propagatedHashCode * prime + localHashCode;
+		   hashCode = (hashCode * 2) & 0xffffffffL;
+		   
+		   // added validator to check if generated component ids are known
+		   if (validator != null){
+			   if (validator.validateReplayID("w" + hashCode)){
+				   System.out.println("ID w" + hashCode + " is valid.");
+				   System.out.println("==> " + currentSpec + " describes a known component.");
+			   }
+			   else{
+				   System.err.println("ID w" + hashCode + " is unknown." );
+				   System.err.println("==> " + currentSpec + " describes an unknown component.");
+				   System.err.println();
+			   }
+		   }
+
+		   if (iterator.hasNext()){
+			   currentSpec = iterator.next();
+			   Integer index = currentSpec.getIndex();
+			   propagatedHashCode = prime * propagatedHashCode
+					   + index.hashCode();
+		   }
+		   else
+			   currentSpec = null;
+			   
+	   }
+
+	   replayID = "e" + hashCode;
+
+	   return replayID;
+   }
+   
+   
+	/**
+	 * Calculates the replayID of a JFCEvent needed for compatibility with guitar suite 
+	 * @param event for which the ID should be calculated
+	 * @return replayID
+	 */
+	public String calculateReplayID(Event event){
+		List<JFCGUIElementSpec> guiElementPath = new ArrayList<JFCGUIElementSpec>();
+		
+		IEventTarget target = event.getTarget();
+		if (!target.getPlatform().equals("JFC")){
+			throw new IllegalArgumentException("Event target must be of type JFC.");
+		}
+		
+		JFCGUIElement currentTarget = (JFCGUIElement) target;
+		
+		// extract element path
+		while (currentTarget != null){
+			JFCGUIElementSpec currentSpec = (JFCGUIElementSpec) currentTarget.getSpecification();
+			
+			// new specification must be inserted at the beginning of the list
+			guiElementPath.add(0, currentSpec);
+			currentTarget = (JFCGUIElement) currentTarget.getParent();
+		}
+		
+		// calculation is delegated to other calculateReplayID method
+		return this.calculateReplayID(guiElementPath);
+	}
+	
+	/**
+	 * Calculates the hashcode part of a component.
+	 * @param spec The {@link JFCGUIElementSpec} for which the hashcode should be calculated.
+	 * @return the local hashcode
+	 */
+	
+	private long getLocalHashCode(JFCGUIElementSpec spec){
+		long hashcode = 1;
+		String wClass = spec.getType();
+		if (IGNORED_CLASSES.contains(wClass)) {
+		    hashcode = (prime * hashcode + (wClass.equals("null") ? 0 : (wClass
+		               .hashCode())));
+		    return hashcode;
+		}
+		else{
+			Map<String, String> idProperties = extractIDProperties(spec);
+			for (String property: idProperties.keySet()){
+				String value = idProperties.get(property);
+				if (!value.equals("null")){
+					hashcode = prime * hashcode + property.hashCode();
+					hashcode = prime * hashcode + value.hashCode();
+				}
+			}
+		}
+		
+		hashcode = (hashcode * 2) & 0xffffffffL;
+		
+		return hashcode;
+	}
+	
+	/**
+	 * Extracts the IDProperties from a given {@link JFCGUIElementSpec}.
+	 * @param spec
+	 * @return LinkedHashMap that contains the IDProperties and its values.
+	 */
+	
+	private Map<String, String> extractIDProperties(JFCGUIElementSpec spec){
+		LinkedHashMap<String, String> idProperties = new LinkedHashMap<String, String>();
+		if (ID_PROPERTIES.contains("Class")){
+			idProperties.put("Class", spec.getType());
+		}
+		if (ID_PROPERTIES.contains("Title")){
+			String name = spec.getName();
+			// spec returns extra "" that need to be removed
+			idProperties.put("Title", name.substring(1, name.length() - 1));
+		}
+		if (ID_PROPERTIES.contains("Icon")){
+			idProperties.put("Icon", spec.getIcon());
+		}
+		if (ID_PROPERTIES.contains("Index")){
+			idProperties.put("Index", Integer.toString(spec.getIndex()));
+		}	
+		return idProperties;
+	}
+	
+	/**
+	 * Guitar has a special way to deal with window titles when
+	 * calculating unique widget IDs. This method mimics Guitar's
+	 * behavior (compare guitar source code: edu.umd.cs.guitar.
+	 * model.JFCDefaultIDGeneratorSimple).
+	 * @param title
+	 * @return fuzzyTitle
+	 */
+
+	private String getFuzzyTitle(String title){
+		final List<String> PATTERNS = 
+				Arrays.asList("Rachota .*",
+						"OmegaT-.*",
+						"Buddi.*",
+						"Open:.*",
+						"JabRef.*",
+						"GanttProject.*",
+						".*Pauker.*",
+						".*FreeMind.*",
+						".* - ArgoUML.*",
+						"Save Project .*");
+
+
+		for (String sPattern : PATTERNS) {
+			if (matchRegex(title, sPattern)) {
+				return sPattern;
+			}
+		}
+
+		return title;
+	}
+
+	/**
+	 * Determine if the input string matches the input regex pattern.
+	 * This method mimics Guitars behavior.
+	 * Attempt to match the pattern 'sPattern' with the string 'sInputString'.
+
+	 * @param sInputString    Input string to match with pattern
+	 * @param sPattern        Regex pattern to match with string
+	 * @return                True if match, false otherwise
+	 */
+	
+	private static boolean
+	matchRegex(String sInputString,
+			String sPattern)
+	{
+		Pattern pattern;
+		Matcher matcher;
+
+		pattern = Pattern.compile(sPattern);
+		matcher = pattern.matcher(sInputString);
+		if (matcher.matches()) {
+			return true;
+		}
+
+		return false;
+	}
+	
+};
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDValidator.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDValidator.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCReplayIDValidator.java	(revision 922)
@@ -0,0 +1,69 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+public class JFCReplayIDValidator {
+	private Set<String> validIds = null;
+	
+	public JFCReplayIDValidator(File guitarEfgFile) throws ParserConfigurationException, SAXException, IOException{
+		this.initialize(guitarEfgFile);
+	}
+	
+	public void initialize(File guitarGUIFile) throws ParserConfigurationException, SAXException, IOException{
+		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+		DocumentBuilder builder = factory.newDocumentBuilder();
+		Document freemindGUI = builder.parse(guitarGUIFile);
+	
+		validIds = new HashSet<String>();
+		
+		NodeList guis = freemindGUI.getFirstChild().getChildNodes();
+		for (int i = 0; i < guis.getLength(); i++)
+			extractIDs(guis.item(i));
+	}
+	
+	private void extractIDs(Node node){
+		NodeList children = node.getChildNodes();
+		if (children != null){
+			for (int j = 0; j < children.getLength(); j++){
+				if (children.item(j).getNodeName().equals("Attributes")){
+					NodeList properties = children.item(j).getChildNodes();
+					for (int i = 0; i < properties.getLength(); i++){
+						NodeList property = properties.item(i).getChildNodes();
+						for (int k = 0; k < property.getLength(); k++){
+							if (property.item(k).getTextContent().equals("ID")){
+								validIds.add(property.item(k+2).getTextContent());
+							}
+						}
+					}
+				}
+				else{
+					extractIDs(children.item(j));
+				}
+			}
+		}
+	}
+	
+
+	public boolean validateReplayID(String replayID){
+		if (validIds == null || validIds.size() == 0)
+			throw new IllegalStateException("There are no valid IDs known, call initialize first.");
+		
+		if (validIds.contains(replayID))
+			return true;
+		
+		return false;
+		
+	}
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCTraceCorrector.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCTraceCorrector.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCTraceCorrector.java	(revision 922)
@@ -0,0 +1,1289 @@
+package de.ugoe.cs.autoquest.plugin.jfc;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * corrects older JFC log files which sometimes do not contain correct source specifications for
+ * events. It parses the file and adds component specifications to the sources, that do not have
+ * them. For each invalid source it checks, if there is another source with the same
+ * <code>toString</code> parameter but a complete list of components. If one is found, it is reused
+ * as source for the event with the wrong source. If none is found, a new component list is
+ * generated. This contains as parent components the components of the source of the previous event.
+ * The leaf component is parsed from the <code>toString</code> parameter that is provided with the
+ * source specifications. The resulting leaf nodes are not fully correct. They may pretend to
+ * be equal although they are not. Furthermore they may resist at a position in the GUI tree where
+ * they are not in reality. But more correctness is not achievable based on the
+ * <code>toString</code> parameter. 
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 05.09.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class JFCTraceCorrector  extends DefaultHandler {
+
+    /**
+     * <p>
+     * the file to write the result into
+     * </p>
+     */
+    private PrintStream outFile;
+    
+    /**
+     * <p>
+     * the currently parsed event
+     * </p>
+     */
+    private Event currentEvent;
+
+    /**
+     * <p>
+     * the currently parsed source of the currently parsed event
+     * </p>
+     */
+    private Source currentSource;
+
+    /**
+     * <p>
+     * the list of all sources parsed in a file identified through their <code>toString</code>
+     * representation
+     * </p>
+     */
+    private Map<String, List<Source>> allSources = new HashMap<String, List<Source>>();
+
+    /**
+     * <p>
+     * the currently parsed component of the currently parsed source of the currently parsed event
+     * </p>
+     */
+    private Component currentComponent;
+
+    /**
+     * <p>
+     * the currently parsed session
+     * </p>
+     */
+    private Session currentSession;
+
+    /**
+     * <p>
+     * corrects the given file and returns the name of the file into which the result was written
+     * </p>
+     * 
+     * @param filename the name of the file to be corrected
+     * 
+     * @return the name of the file with the corrected logfile
+     * 
+     * @throws IllegalArgumentException if the filename is null
+     */
+    public String correctFile(String filename) throws IllegalArgumentException {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename must not be null");
+        }
+
+        return correctFile(new File(filename)).getAbsolutePath();
+    }
+
+    /**
+     * <p>
+     * corrects the given file, stores the result in the second provided file and returns the
+     * name of the file into which the result was written
+     * </p>
+     * 
+     * @param filename   the name of the file to be corrected
+     * @param resultFile the name of the file into which the corrected log shall be written
+     * 
+     * @return the name of the file with the corrected logfile
+     * 
+     * @throws IllegalArgumentException if the filename or resultFile is null
+     */
+    public String correctFile(String filename, String resultFile) throws IllegalArgumentException {
+        if ((filename == null) | (resultFile == null)) {
+            throw new IllegalArgumentException("filename and resultFile must not be null");
+        }
+
+        return correctFile(new File(filename), new File(resultFile)).getAbsolutePath();
+    }
+
+    /**
+     * <p>
+     * corrects the given file and returns the file into which the result was written. The name
+     * of the resulting file is contains the suffix "_corrected" before the dot.
+     * </p>
+     * 
+     * @param file the file to be corrected
+     * 
+     * @return the file containing the corrected logfile
+     * 
+     * @throws IllegalArgumentException if the file is null
+     */
+    public File correctFile(File file) throws IllegalArgumentException {
+        if (file == null) {
+            throw new IllegalArgumentException("file must not be null");
+        }
+
+        int index = file.getName().lastIndexOf('.');
+        String fileName =
+            file.getName().substring(0, index) + "_corrected" + file.getName().substring(index);
+
+        File resultFile = new File(file.getParentFile(), fileName);
+
+        return correctFile(file, resultFile);
+    }
+
+    /**
+     * <p>
+     * corrects the given file, stores the result in the second provided file and returns the
+     * file into which the result was written
+     * </p>
+     * 
+     * @param file       the file to be corrected
+     * @param resultFile the file into which the corrected log shall be written
+     * 
+     * @return the file with the corrected logfile
+     * 
+     * @throws IllegalArgumentException if the file or resultFile is null or if they are equal
+     */
+    public File correctFile(File file, File resultFile) throws IllegalArgumentException {
+        if ((file == null) || (resultFile == null)) {
+            throw new IllegalArgumentException("file and result file must not be null");
+        }
+        
+        if (file.getAbsolutePath().equals(resultFile.getAbsolutePath())) {
+            throw new IllegalArgumentException("file and result file must not be equal");
+        }
+        
+        try {
+            outFile = new PrintStream(new BufferedOutputStream(new FileOutputStream(resultFile)));
+            outFile.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
+        }
+        catch (FileNotFoundException e1) {
+            throw new IllegalArgumentException("could not create a corrected file name " +
+                                               resultFile + " next to " + file);
+        }
+        
+
+        SAXParserFactory spf = SAXParserFactory.newInstance();
+        spf.setValidating(true);
+
+        SAXParser saxParser = null;
+        InputSource inputSource = null;
+        try {
+            saxParser = spf.newSAXParser();
+            inputSource =
+                new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8"));
+        }
+        catch (UnsupportedEncodingException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return null;
+        }
+        catch (ParserConfigurationException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return null;
+        }
+        catch (SAXException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return null;
+        }
+        catch (FileNotFoundException e) {
+            Console.printerr("Error parsing file + " + file.getName());
+            Console.logException(e);
+            return null;
+        }
+        if (inputSource != null) {
+            inputSource.setSystemId("file://" + file.getAbsolutePath());
+            try {
+                if (saxParser == null) {
+                    throw new RuntimeException("SAXParser creation failed");
+                }
+                saxParser.parse(inputSource, this);
+            }
+            catch (SAXParseException e) {
+                Console.printerrln("Failure parsing file in line " + e.getLineNumber() +
+                    ", column " + e.getColumnNumber() + ".");
+                Console.logException(e);
+                return null;
+            }
+            catch (SAXException e) {
+                Console.printerr("Error parsing file + " + file.getName());
+                Console.logException(e);
+                return null;
+            }
+            catch (IOException e) {
+                Console.printerr("Error parsing file + " + file.getName());
+                Console.logException(e);
+                return null;
+            }
+        }
+        
+        if (outFile != null) {
+            outFile.close();
+        }
+        
+        return resultFile;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
+     * java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String uri, String localName, String qName, Attributes atts)
+        throws SAXException
+    {
+        if (qName.equals("sessions")) {
+            if (currentSession != null) {
+                throw new SAXException("nested sessions are not allowed");
+            }
+            
+            currentSession = new Session("sessions");
+        }
+        else if (qName.equals("newsession")) {
+            if (currentSession != null) {
+                currentSession.dump(outFile);
+            }
+            
+            currentSession = new Session("newsession");
+        }
+        else if (qName.equals("event")) {
+            if (currentEvent != null) {
+                throw new SAXException("nested events are not allowed");
+            }
+            
+            currentEvent = new Event(atts.getValue("id"));
+        }
+        else if (qName.equals("source")) {
+            if (currentSource != null) {
+                throw new SAXException("nested sources are not allowed");
+            }
+            
+            currentSource = new Source();
+        }
+        else if (qName.equals("component")) {
+            if (currentComponent != null) {
+                throw new SAXException("nested components are not allowed");
+            }
+            
+            currentComponent = new Component();
+        }
+        else if (qName.equals("param")) {
+            if (currentComponent != null) {
+                currentComponent.addParameter(atts.getValue("name"), atts.getValue("value"));
+            }
+            else if (currentSource != null) {
+                currentSource.addParameter(atts.getValue("name"), atts.getValue("value"));
+            }
+            else if (currentEvent != null) {
+                currentEvent.addParameter(atts.getValue("name"), atts.getValue("value"));
+            }
+            else {
+                throw new SAXException("parameter occurred at an unexpected place");
+            }
+        }
+        else {
+            throw new SAXException("unexpected tag " + qName);
+        }
+    }
+
+    /*
+     * (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 {
+        // we do not have to check, if the current objects are null, as the Parser itself will
+        // throw an exception, if there is a closing tag for a non existent opening tag. But
+        // with a correct opening tag, the member variables will not be null (see startElement())
+        if (qName.equals("sessions")) {
+            correctSources(currentSession);
+
+            currentSession.dump(outFile);
+            currentSession = null;
+            allSources.clear();
+        }
+        else if (qName.equals("newsession")) {
+            correctSources(currentSession);
+            
+            currentSession.dump(outFile);
+            currentSession = null;
+            allSources.clear();
+        }
+        else if (qName.equals("event")) {
+            currentSession.addEvent(currentEvent);
+            currentEvent = null;
+        }
+        else if (qName.equals("source")) {
+            currentEvent.setSource(getUniqueSource(currentSource));
+            currentSource = null;
+        }
+        else if (qName.equals("component")) {
+            currentSource.addComponent(currentComponent);
+            currentComponent = null;
+        }
+        else if (!qName.equals("param")) {
+            throw new SAXException("unexpected closing tag " + qName);
+        }
+
+    }
+
+    /**
+     * <p>
+     * returns a source object, that is equal to the provided one but that is unique throughout
+     * the parsing process. The method may return the provided source, if this is the first
+     * occurrence of this source. This method is needed to reduce the amount of source
+     * representations that are instantiated during parsing log files. 
+     * </p>
+     *
+     * @param source the source to search for a unique representation
+     * 
+     * @return the unique representation of the source
+     */
+    private Source getUniqueSource(Source source) {
+        Source existingSource = null;
+        
+        List<Source> candidates = allSources.get(source.getToStringValue());
+        
+        if (candidates != null) {
+            for (Source candidate : candidates) {
+                if (candidate.equals(source)) {
+                    existingSource = candidate;
+                    break;
+                }
+            }
+        }
+        
+        if (existingSource == null) {
+            if (candidates == null) {
+                candidates = new ArrayList<Source>();
+                allSources.put(source.getToStringValue(), candidates);
+            }
+            
+            candidates.add(source);
+            existingSource = source;
+        }
+        
+        return existingSource;
+    }
+
+    /**
+     * <p>
+     * convenience method to find a source based on its <code>toString</code> parameter value.
+     * The method only returns sources, which match the provided <code>toString</code>
+     * representation and which have a valid list of components (size greater 0).
+     * </p>
+     *
+     * @param toStringValue the value of the <code>toString</code> parameter the source to find
+     *                      must have
+     * 
+     * @return the source matching the parameter and having a valid list of components or null if
+     *         none is found
+     */
+    private Source findValidSource(String toStringValue) {
+        Source existingSource = null;
+        
+        List<Source> candidates = allSources.get(toStringValue);
+        
+        if (candidates != null) {
+            for (Source candidate : candidates) {
+                if ((candidate.getComponents() != null) && (candidate.getComponents().size() > 0))
+                {
+                    existingSource = candidate;
+                    break;
+                }
+            }
+        }
+        
+        return existingSource;
+    }
+
+    /**
+     * <p>
+     * corrects all wrong sources in the events of the session. For each wrong resource, the
+     * {@link #correctEventSource(Event, Source)} method is called.
+     * </p>
+     *
+     * @param session the session of which the events shall be corrected
+     */
+    private void correctSources(Session session) {
+        Source previousSource = null;
+        for (Event event : session.getEvents()) {
+            if ((event.getSource() == null) || (event.getSource().getComponents() == null) ||
+                (event.getSource().getComponents().size() == 0))
+            {
+                correctEventSource(event, previousSource);
+            }
+            
+            previousSource = event.getSource();
+        }
+    }
+
+    /**
+     * <p>
+     * corrects the source of an event. It first searches for a correct source with an equal
+     * <code>toString</code> parameter. If there is any, this is reused. Otherwise it creates a
+     * new correct source. For this, it copies all parameters of the source of the provided previous
+     * event if they are not included in the source already. Furthermore, it adds all components
+     * of the source of the previous event. At last, it adds a further component based on the
+     * information found in the <code>toString</code> parameter of the source.
+     * </p>
+     * 
+     * @param event          the event of which the source must be corrected
+     * @param previousSource the source of the previous event to be potentially reused partially
+     */
+    private void correctEventSource(Event event, Source previousSource) {
+        Source existingSource = null;
+        
+        if ((event.getSource() != null) && (event.getSource().getToStringValue() != null)) {
+            existingSource = findValidSource(event.getSource().getToStringValue());
+        }
+        
+        if (existingSource != null) {
+            event.setSource(existingSource);
+        }
+        else {
+            if (previousSource != null) {
+                for (Parameter parameterOfPreviousSource : previousSource.getParameters()) {
+                    boolean foundParameter = false;
+                    for (Parameter parameter : event.getSource().getParameters()) {
+                        if (parameter.getName().equals(parameterOfPreviousSource.getName())) {
+                            foundParameter = true;
+                            break;
+                        }
+                    }
+
+                    if (!foundParameter) {
+                        event.getSource().addParameter(parameterOfPreviousSource);
+                    }
+                }
+    
+                for (Component component : previousSource.getComponents()) {
+                    if (!(component instanceof ComponentFromToString)) {
+                        event.getSource().addComponent(component);
+                    }
+                }
+            }
+
+            event.getSource().addComponent
+                (getComponentFromToString(event.getSource().getToStringValue()));
+        }
+    }
+
+    /**
+     * <p>
+     * determines a component based on the <code>toString</code> parameter of a source.
+     * For this, it parses the parameter value and tries to determine several infos such as the
+     * type and the name of it. The resulting components are not always distinguishable. This is,
+     * because the <code>toString</code> parameter does not contain sufficient information for
+     * correct identification.
+     * </p>
+     * 
+     * @param toStringValue the <code>toString</code> parameter of a source
+     * 
+     * @return the component parsed from the <code>toString</code> parameter
+     */
+    private Component getComponentFromToString(String toStringValue) {
+        ComponentFromToString component = new ComponentFromToString();
+        
+        // search for the beginning of the parameters section. Up to this position we find the class
+        int start = toStringValue.indexOf('[');
+        String clazz = toStringValue.substring(0, start);
+        
+        // the first parameters are x and y coordinate as well as the size. The size is one
+        // parameter, where with and height are separated with an 'x'
+        start = toStringValue.indexOf(',', start) + 1;
+        int end = toStringValue.indexOf(',', start);
+        
+        component.setX(Integer.parseInt(toStringValue.substring(start, end)));
+        
+        start = end + 1;
+        end = toStringValue.indexOf(',', start);
+
+        component.setY(Integer.parseInt(toStringValue.substring(start, end)));
+
+        start = end + 1;
+        end = toStringValue.indexOf('x', start);
+
+        component.setWidth(Integer.parseInt(toStringValue.substring(start, end)));
+
+        start = end + 1;
+        end = toStringValue.indexOf(',', start);
+
+        component.setHeight(Integer.parseInt(toStringValue.substring(start, end)));
+
+        // no start parsing the rest of the parameters and extract those having a key and a 
+        // value and whose key is text, defaultIcon, or an alignment
+        int intermediate;
+        start = end + 1;
+
+        String title = null;
+        String icon = null;
+        String alignment = null;
+        
+        do {
+            end = toStringValue.indexOf(',', start);
+            intermediate = toStringValue.indexOf('[', start);
+            
+            if ((intermediate >= 0) && (intermediate < end)) {
+                // the value of the parameter itself contains brackets. So try to determine the 
+                // real end of the parameter
+                end = toStringValue.indexOf(']', intermediate);
+                end = toStringValue.indexOf(',', end);
+            }
+            
+            if (end < 0) {
+                //we reached the end of the stream. So the the end to the "end"
+                end = toStringValue.lastIndexOf(']');
+            }
+            
+            intermediate = toStringValue.indexOf('=', start);
+            
+            if ((intermediate >= 0) && (intermediate < end)) {
+                // this is a key value pair, so store the the parameter
+                String key = toStringValue.substring(start, intermediate);
+                String value = toStringValue.substring(intermediate + 1, end);
+                
+                if ("text".equals(key)) {
+                    title = value;
+                }
+                else if ("defaultIcon".equals(key)) {
+                    icon = value;
+                }
+                else if ("alignmentX".equals(key) || "alignmentY".equals(key)) {
+                    if (alignment == null) {
+                        alignment = value;
+                    }
+                    else {
+                        alignment += "/" + value;
+                    }
+                }
+            }
+            /*else {
+                // this is a simple value, for now simply ignore it
+                String key = toStringValue.substring(start, end);
+                if (!"invalid".equals(key)) {
+                    componentHash += key.hashCode();
+                    component.params.add(new String[] { key, "true" });
+                }
+            }*/
+            
+            start = end + 1;
+        }
+        while (start < toStringValue.lastIndexOf(']'));
+        
+        // finish the component specification by setting the parameters
+        if ((title == null) || "".equals(title) || "null".equals(title)) {
+            if ((icon == null) || "".equals(icon) || "null".equals(icon)) {
+                title = clazz.substring(clazz.lastIndexOf('.') + 1) + "(";
+                
+                // to be able to distinguish some elements, that usually have no name and icon, try
+                // to include some of their specific identifying information in their name.
+                if ("org.tigris.gef.presentation.FigTextEditor".equals(clazz) ||
+                    "org.argouml.core.propertypanels.ui.UMLTextField".equals(clazz))
+                {
+                    title += "height " + component.height + ", ";
+                }
+                else if ("org.argouml.core.propertypanels.ui.UMLLinkedList".equals(clazz) ||
+                         "org.argouml.core.propertypanels.ui.LabelledComponent".equals(clazz))
+                {
+                    title += "position " + component.getX() + "/" + component.getY() + ", ";
+                }
+                
+                title += "alignment " + alignment + ")";
+            }
+            else {
+                // to be able to distinguish some elements, that usually have no name but an icon,
+                // try to include some of their specific identifying information in their name.
+                if ("org.tigris.toolbar.toolbutton.PopupToolBoxButton".equals(clazz))
+                {
+                    icon = icon.substring(0, icon.lastIndexOf('@'));
+                    title = clazz.substring(clazz.lastIndexOf('.') + 1) + "(position " +
+                        component.getX() + ")";
+                }
+                else {
+                    title = icon;
+                }
+            }
+        }
+        
+        component.addParameter("title", title);
+        component.addParameter("class", clazz);
+        component.addParameter("icon", ((icon == null) ? "" : icon));
+        component.addParameter("index", "-1");
+        
+        int hashCode = clazz.hashCode() + title.hashCode();
+        
+        if (hashCode < 0) {
+            hashCode = -hashCode;
+        }
+        
+        component.addParameter("hash", Integer.toString(hashCode, 16));
+
+        return component;
+    }    
+
+    /**
+     * <p>
+     * used to dump a list of parameters to the provided print stream
+     * </p>
+     */
+    private void dumpParams(PrintStream out, List<Parameter> params, String indent) {
+        for (Parameter param : params) {
+            out.print(indent);
+            out.print("<param name=\"");
+            out.print(StringTools.xmlEntityReplacement(param.getName()));
+            out.print("\" value=\"");
+            out.print(StringTools.xmlEntityReplacement(param.getValue()));
+            out.println("\" />");
+        }
+        
+    }
+    
+    /**
+     * <p>
+     * check if two parameter lists are equal. Thea are equal if the contain the same parameters
+     * ignoring their order.
+     * </p>
+     *
+     * @param params1 the first parameter list to be compared
+     * @param params2 the second parameter list to be compared
+     * 
+     * @return true if both lists contain the same parameters, false else.
+     */
+    private boolean parametersEqual(List<Parameter> params1, List<Parameter> params2) {
+        if (params1 == null) {
+            return params2 == null;
+        }
+        
+        if (params2 == null) {
+            return false;
+        }
+        
+        if (params1.size() != params2.size()) {
+            return false;
+        }
+        
+        boolean found;
+        for (Parameter param1 : params1) {
+            found = false;
+            
+            for (Parameter param2 : params2) {
+                if (param1.equals(param2)) {
+                    found = true;
+                    break;
+                }
+            }
+            
+            if (!found) {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    
+    /**
+     * <p>
+     * used to carry all events of a session and to dump it to the output file
+     * </p> 
+     */
+    private class Session {
+        
+        /** */
+        private String type;
+        
+        /** */
+        private List<Event> events = new ArrayList<Event>();
+        
+        /**
+         * 
+         */
+        private Session(String type) {
+            if (type == null) {
+                throw new IllegalArgumentException("type must not be null");
+            }
+            
+            this.type = type;
+        }
+        
+        /**
+         * 
+         */
+        private void addEvent(Event event) {
+            if (event == null) {
+                throw new IllegalArgumentException("event must not be null");
+            }
+            
+            events.add(event);
+        }
+        
+        /**
+         * @return the events
+         */
+        private List<Event> getEvents() {
+            return Collections.unmodifiableList(events);
+        }
+
+        /**
+         * 
+         */
+        private void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
+            out.print("<");
+            out.print(type);
+            out.println(">");
+
+            for (Event event : events) {
+                event.dump(out);
+            }
+            
+            out.print("</");
+            out.print(type);
+            out.println(">");
+        }
+    }
+
+    /**
+     * <p>
+     * used to carry all information about an event and to dump it to the output file
+     * </p> 
+     */
+    private class Event {
+
+        /** */
+        private String id;
+
+        /** */
+        private List<Parameter> params = new ArrayList<Parameter>();
+
+        /** */
+        private Source source;
+        
+        /**
+         * 
+         */
+        private Event(String id) {
+            if (id == null) {
+                throw new IllegalArgumentException("id must not be null");
+            }
+            
+            this.id = id;
+        }
+        
+        /**
+         * @param source the source to set
+         */
+        private void setSource(Source source) {
+            this.source = source;
+        }
+
+        /**
+         * @return the source
+         */
+        private Source getSource() {
+            return source;
+        }
+
+        /**
+         * 
+         */
+        private void addParameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            params.add(new Parameter(name, value));
+        }
+        
+        /**
+         * 
+         */
+        private void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
+            out.print("<event id=\"");
+            out.print(StringTools.xmlEntityReplacement(id));
+            out.println("\">");
+            
+            dumpParams(out, params, " ");
+            source.dump(out);
+            
+            out.println("</event>");
+        }
+    }
+
+    /**
+     * <p>
+     * used to carry all information about a source of an event and to dump it to the output file
+     * </p> 
+     */
+    private class Source {
+        
+        /** */
+        private List<Parameter> params = new ArrayList<Parameter>();
+        
+        /** */
+        private List<Component> components = new ArrayList<Component>();
+        
+        /** */
+        private String toStringValue = null;
+
+        /**
+         *
+         */
+        private String getToStringValue() {
+            if (toStringValue == null) {
+                for (Parameter param : params) {
+                    if (("toString".equals(param.getName())) &&
+                        (param.getValue() != null) && (!"".equals(param.getValue())))
+                    {
+                        toStringValue = param.getValue();
+                        break;
+                    }
+                }
+            }
+            
+            return toStringValue;
+        }
+
+        /**
+         * 
+         */
+        private void addParameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            params.add(new Parameter(name, value));
+        }
+        
+        /**
+         * 
+         */
+        private void addParameter(Parameter parameter) {
+            if (parameter == null) {
+                throw new IllegalArgumentException("parameter must not be null");
+            }
+            
+            params.add(parameter);
+        }
+        
+        /**
+         * @return the params
+         */
+        private List<Parameter> getParameters() {
+            return Collections.unmodifiableList(params);
+        }
+
+        /**
+         * 
+         */
+        private void addComponent(Component component) {
+            if (component == null) {
+                throw new IllegalArgumentException("component must not be null");
+            }
+            
+            components.add(component);
+        }
+        
+        /**
+         * @return the components
+         */
+        private List<Component> getComponents() {
+            return Collections.unmodifiableList(components);
+        }
+
+        /**
+         * 
+         */
+        private void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
+            out.println(" <source>");
+            
+            dumpParams(out, params, "  ");
+            
+            for (Component component : components) {
+                component.dump(out);
+            }
+            
+            out.println(" </source>");
+        }
+        
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            
+            if (obj instanceof Source) {
+                Source other = (Source) obj;
+                
+                if ((getToStringValue() != other.getToStringValue()) ||
+                    ((getToStringValue() != null) &&
+                     (!getToStringValue().equals(other.getToStringValue()))))
+                {
+                    return false;
+                }
+                
+                if (!parametersEqual(params, other.params)) {
+                    return false;
+                }
+                
+                if (components == null) {
+                    return other.components == null;
+                }
+                
+                if (other.components == null) {
+                    return false;
+                }
+                
+                if (components.size() != other.components.size()) {
+                    return false;
+                }
+
+                for (int i = 0; i < components.size(); i++) {
+                    if (!components.get(i).equals(other.components.get(i))) {
+                        return false;
+                    }
+                }
+                
+                return true;
+            }
+            
+            return false;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            String str = getToStringValue();
+            
+            if (str != null) {
+                return str.hashCode();
+            }
+            else {
+                // ensure that all incomplete sources provide the same hashcode
+                return 0;
+            }
+        }
+
+    }
+    
+    /**
+     * <p>
+     * used to carry all information about a component of a source and to dump it to the output file
+     * </p> 
+     */
+    private class Component {
+        
+        /** */
+        private List<Parameter> params = new ArrayList<Parameter>();
+        
+        /**
+         * 
+         */
+        protected void addParameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            params.add(new Parameter(name, value));
+        }
+        
+        /**
+         * @return the params
+         */
+        private List<Parameter> getParameters() {
+            return Collections.unmodifiableList(params);
+        }
+
+        /**
+         * 
+         */
+        protected void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
+            out.println("  <component>");
+            dumpParams(out, params, "   ");
+            out.println("  </component>");
+        }
+
+        /**
+         * 
+         */
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            
+            if (obj instanceof Component) {
+                return parametersEqual(params, ((Component) obj).params);
+            }
+            else {   
+               return false;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            // all components with an equally sized parameter list can be equal. This does not
+            // work, if not all component parameters are set yet. But we do not use components
+            // in a hash map so we provide an easy implementation
+            return params.size();
+        }
+    }
+
+    /**
+     * <p>
+     * represents a specific component, which was read from the toString parameter of a source
+     * </p> 
+     */
+    private class ComponentFromToString extends Component {
+        
+        /** */
+        private int x;
+        
+        /** */
+        private int y;
+        
+        /** */
+        private int width;
+        
+        /** */
+        private int height;
+        
+        /**
+         * @param x the x to set
+         */
+        private void setX(int x) {
+            this.x = x;
+        }
+
+        /**
+         * @return the x
+         */
+        private int getX() {
+            return x;
+        }
+
+       /**
+         * @param y the y to set
+         */
+        private void setY(int y) {
+            this.y = y;
+        }
+
+        /**
+         * @return the y
+         */
+        private int getY() {
+            return y;
+        }
+
+        /**
+         * @param width the width to set
+         */
+        private void setWidth(int width) {
+            this.width = width;
+        }
+
+        /**
+         * @param height the height to set
+         */
+        private void setHeight(int height) {
+            this.height = height;
+        }
+
+        /**
+         * 
+         */
+        @Override
+        protected void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
+            out.println("  <component>");
+            
+            out.print("   ");
+            out.print("<param name=\"x\" value=\"");
+            out.print(x);
+            out.println("\" />");
+
+            out.print("   ");
+            out.print("<param name=\"y\" value=\"");
+            out.print(y);
+            out.println("\" />");
+
+            out.print("   ");
+            out.print("<param name=\"width\" value=\"");
+            out.print(width);
+            out.println("\" />");
+
+            out.print("   ");
+            out.print("<param name=\"height\" value=\"");
+            out.print(height);
+            out.println("\" />");
+
+            dumpParams(out, super.getParameters(), "   ");
+            out.println("  </component>");
+        }
+        
+        /**
+         * 
+         */
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            
+            if (obj instanceof ComponentFromToString) {
+                ComponentFromToString other = (ComponentFromToString) obj;
+                return (x == other.x) && (y == other.y) &&
+                    (width == other.width) && (height == other.height);
+            }
+            else {   
+               return false;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return super.hashCode() + x + y + width + height;
+        }
+    }
+
+    /**
+     * <p>
+     * used to carry all information about a parameter being a key and a value
+     * </p> 
+     */
+    private class Parameter {
+        
+        /** */
+        private String name;
+        
+        /** */
+        private String value;
+        
+        /**
+         * 
+         */
+        private Parameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            this.name = name;
+            this.value = value;
+        }
+
+        /**
+         * @return the name
+         */
+        private String getName() {
+            return name;
+        }
+
+        /**
+         * @return the value
+         */
+        private String getValue() {
+            return value;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Parameter) {
+                return
+                    (name.equals(((Parameter) obj).name) && value.equals(((Parameter) obj).value));
+            }
+            else {
+                return false;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return name.hashCode() + value.hashCode();
+        }
+        
+        
+    }
+        
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDcorrectDirOldJFC.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDcorrectDirOldJFC.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDcorrectDirOldJFC.java	(revision 922)
@@ -0,0 +1,77 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.commands;
+
+import java.io.File;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.plugin.jfc.JFCTraceCorrector;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command that tries to correct all files in a folder as if they were log files generated by the
+ * older version of the JFCMonitor which sometimes do not include correct event sources. The result
+ * is another specified directory, which contains the corrected files.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDcorrectDirOldJFC implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String path;
+        String resultPath;
+
+        try {
+            path = (String) parameters.get(0);
+            resultPath = (String) parameters.get(1);
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+
+        File folder = new File(path);
+        if (!folder.isDirectory()) {
+            Console.printerrln(path + " is not a directory");
+            return;
+        }
+
+        File destfolder = new File(resultPath);
+        if (!destfolder.isDirectory()) {
+            Console.printerrln(resultPath + " is not a directory");
+            return;
+        }
+
+        JFCTraceCorrector corrector = new JFCTraceCorrector();
+
+        String absolutPath = folder.getAbsolutePath();
+        String absolutDestPath = destfolder.getAbsolutePath();
+        for (String filename : folder.list()) {
+            String source = absolutPath + "/" + filename;
+            String dest = absolutDestPath + "/" + filename;
+            Console.traceln(Level.INFO, "Processing file: " + source);
+
+            corrector.correctFile(source, dest);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "correctDirOldJFC <sourcedirectory> <destinationdirectory>";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDcorrectOldJFC.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDcorrectOldJFC.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDcorrectOldJFC.java	(revision 922)
@@ -0,0 +1,53 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.commands;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.plugin.jfc.JFCTraceCorrector;
+import de.ugoe.cs.util.console.Command;
+
+/**
+ * <p>
+ * Command to correct an XML file with sessions monitored by the older version of the
+ * EventBench's JFCMonitor which sometimes do not include correct event sources.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDcorrectOldJFC implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String inputfilename;
+        String outputfilename;
+
+        try {
+            inputfilename = (String) parameters.get(0);
+            outputfilename = (String) parameters.get(1);
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+
+        JFCTraceCorrector corrector = new JFCTraceCorrector();
+
+        corrector.correctFile(inputfilename, outputfilename);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "correctOldJFC <inputfilename> <outputfilename>";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDparseDirJFC.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDparseDirJFC.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDparseDirJFC.java	(revision 922)
@@ -0,0 +1,87 @@
+package de.ugoe.cs.autoquest.plugin.jfc.commands;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+
+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.jfc.JFCLogParser;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command that tries to parse all files in a folder as if they were log files generated by the
+ * JFCMonitor. The result is one set of sequences for all files (not one set of sequences for each
+ * file!).
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDparseDirJFC implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String path;
+        String sequencesName = "sequences";
+
+        try {
+            path = (String) parameters.get(0);
+            if (parameters.size() >= 2) {
+                sequencesName = (String) parameters.get(1);
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+
+        File folder = new File(path);
+        if (!folder.isDirectory()) {
+            Console.printerrln(path + " is not a directory");
+            return;
+        }
+
+        JFCLogParser parser = new JFCLogParser();
+
+        String absolutPath = folder.getAbsolutePath();
+        for (String filename : folder.list()) {
+            String source = absolutPath + "/" + filename;
+            Console.traceln(Level.INFO, "Processing file: " + source);
+
+            parser.parseFile(source);
+        }
+
+        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");
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "parseDirJFC <directory> {<sequencesName>}";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDparseJFC.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDparseJFC.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDparseJFC.java	(revision 922)
@@ -0,0 +1,69 @@
+package de.ugoe.cs.autoquest.plugin.jfc.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.jfc.JFCLogParser;
+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
+ * JFCMonitor.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDparseJFC implements Command {
+
+	/*
+	 * (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";
+
+		try {
+			filename = (String) parameters.get(0);
+			if (parameters.size() >= 2) {
+				sequencesName = (String) parameters.get(1);
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+		
+		JFCLogParser parser = new JFCLogParser();
+
+		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");
+	        }
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "parseJFC <filename> {<sequencesName>}";
+	}
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDpreprocessDirJFC.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDpreprocessDirJFC.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDpreprocessDirJFC.java	(revision 922)
@@ -0,0 +1,119 @@
+package de.ugoe.cs.autoquest.plugin.jfc.commands;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command to pre-process files written by EventBench's JFCMonitor located in a
+ * directory. The only task of the pre-processing is checking if the session was
+ * closed properly, i.e., if the XML file ends with a {@code </sessions>} tag.
+ * If this is not the case, the tag will be appended to the file.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDpreprocessDirJFC implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String sourcePath;
+		String targetPath;
+		try {
+			sourcePath = (String) parameters.get(0);
+			targetPath = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		File sourceFolder = new File(sourcePath);
+		if (!sourceFolder.isDirectory()) {
+			Console.printerrln(sourcePath + " is not a directory");
+		}
+		String absolutPathSource = sourceFolder.getAbsolutePath();
+		File targetFolder = new File(targetPath);
+		if (!targetFolder.isDirectory()) {
+			Console.printerrln(targetPath + " is not a directory");
+		}
+		String absolutPathTarget = targetFolder.getAbsolutePath();
+
+		for (String filename : sourceFolder.list()) {
+			String source = absolutPathSource + "/" + filename;
+			Console.traceln(Level.INFO, "Preprocessing file: " + source);
+			File file = new File(source);
+			InputStreamReader reader;
+			try {
+				FileInputStream fis = new FileInputStream(file);
+				reader = new InputStreamReader(fis, "UTF-16");
+			} catch (FileNotFoundException e) {
+				Console.printerrln(e.getMessage());
+				return;
+			} catch (UnsupportedEncodingException e) {
+				Console.printerrln(e.getMessage());
+				return;
+			}
+			char[] buffer = new char[(int) file.length()];
+			try {
+				reader.read(buffer);
+				reader.close();
+			} catch (IOException e) {
+				Console.printerrln(e.getMessage());
+				return;
+			}
+
+			String content = new String(buffer).trim();
+
+			int index = filename.lastIndexOf('.');
+			String target = absolutPathTarget + "/"
+					+ filename.substring(0, index) + ".xml";
+
+			Console.traceln(Level.INFO, "   Saving as: " + target);
+
+			OutputStreamWriter writer;
+			try {
+				FileOutputStream fos = new FileOutputStream(target);
+				writer = new OutputStreamWriter(fos, "UTF-8");
+			} catch (IOException e) {
+				Console.printerrln(e.getMessage());
+				return;
+			}
+			try {
+				writer.write(content);
+				if (!content.endsWith("</sessions>")) {
+					writer.write("</sessions>");
+				}
+				writer.close();
+			} catch (IOException e) {
+				Console.printerrln(e.getMessage());
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "preprocessDirJFC <sourceDirectory> <targetDirectory>";
+	}
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDpreprocessJFC.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDpreprocessJFC.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDpreprocessJFC.java	(revision 922)
@@ -0,0 +1,97 @@
+package de.ugoe.cs.autoquest.plugin.jfc.commands;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command to pre-process files written by EventBench's JFCMonitor. The only
+ * task of the pre-processing is checking if the session was closed properly,
+ * i.e., if the XML file ends with a {@code </sessions>} tag. If this is not the
+ * case, the tag will be appended to the file.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDpreprocessJFC implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String source;
+		String target;
+		try {
+			source = (String) parameters.get(0);
+			target = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		File file = new File(source);
+		InputStreamReader reader = null;
+		try {
+			FileInputStream fis = new FileInputStream(file);
+			reader = new InputStreamReader(fis, "UTF-16");
+		} catch (FileNotFoundException e) {
+			Console.printerrln(e.getMessage());
+			return;
+		} catch (UnsupportedEncodingException e) {
+			Console.printerrln(e.getMessage());
+			return;
+		}
+		char[] buffer = new char[(int) file.length()];
+		try {
+			reader.read(buffer);
+			reader.close();
+		} catch (IOException e) {
+			Console.printerrln(e.getMessage());
+			return;
+		}
+
+		String content = new String(buffer).trim();
+
+		OutputStreamWriter writer;
+		try {
+			FileOutputStream fos = new FileOutputStream(target);
+			writer = new OutputStreamWriter(fos, "UTF-8");
+		} catch (IOException e) {
+			Console.printerrln(e.getMessage());
+			return;
+		}
+		try {
+			writer.write(content);
+			if (!content.endsWith("</sessions>")) {
+				writer.write("</sessions>");
+			}
+			writer.close();
+		} catch (IOException e) {
+			Console.printerrln(e.getMessage());
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "preprocessJFC <sourceFile> <targetFile>";
+	}
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/eventcore/JFCEventId.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/eventcore/JFCEventId.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/eventcore/JFCEventId.java	(revision 922)
@@ -0,0 +1,97 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.eventcore;
+
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+/**
+ * <p>
+ * Enumeration to deal with JFC event ids.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public enum JFCEventId {
+
+    MOUSE_CLICKED(MouseEvent.MOUSE_CLICKED), MOUSE_PRESSED(MouseEvent.MOUSE_PRESSED),
+    MOUSE_RELEASED(MouseEvent.MOUSE_RELEASED), MOUSE_MOVED(MouseEvent.MOUSE_MOVED), MOUSE_ENTERED(
+        MouseEvent.MOUSE_ENTERED), MOUSE_EXITED(MouseEvent.MOUSE_EXITED), MOUSE_DRAGGED(
+        MouseEvent.MOUSE_DRAGGED), MOUSE_WHEEL(MouseEvent.MOUSE_WHEEL), FOCUS_GAINED(
+        FocusEvent.FOCUS_GAINED), FOCUS_LOST(FocusEvent.FOCUS_LOST), KEY_TYPED(KeyEvent.KEY_TYPED),
+    KEY_PRESSED(KeyEvent.KEY_PRESSED), KEY_RELEASED(KeyEvent.KEY_RELEASED);
+
+    /**
+     * <p>
+     * Numerical representation of the event type.
+     * </p>
+     */
+    private int mNumber;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCEventId.
+     * </p>
+     * 
+     * @param number
+     *            numerical representation of the event type.
+     */
+    JFCEventId(int number) {
+        mNumber = number;
+    }
+
+    /**
+     * <p>
+     * Returns the numerical representation of the event type.
+     * </p>
+     * 
+     * @return the numerical representation
+     */
+    public int getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * <p>
+     * Parses an {@link String} and returns the respective JFCEventId if possible.
+     * </p>
+     * 
+     * @param numberString
+     *            String representation of the event type
+     * @return created JFCEventId
+     * @throws IllegalArgumentException
+     *             thrown if there is no JFCEventId that correlates to numberString
+     */
+    public static JFCEventId parseEventId(String numberString) throws IllegalArgumentException {
+        try {
+            int number = Integer.parseInt(numberString);
+            return valueOf(number);
+        }
+        catch (NumberFormatException e) {
+            return JFCEventId.valueOf(JFCEventId.class, numberString);
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the JFCEventId associated with an integer.
+     * </p>
+     * 
+     * @param number
+     *            integer to which the according JFCEventId is returned
+     * @return the JFCEventId
+     * @throws IllegalArgumentException
+     *             thrown if there is no JFCEventId that correlates to number
+     */
+    public static JFCEventId valueOf(int number) throws IllegalArgumentException {
+        for (JFCEventId type : JFCEventId.values()) {
+            if (type.mNumber == number) {
+                return type;
+            }
+        }
+
+        throw new IllegalArgumentException("there is no event type with number " + number);
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCButton.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCButton.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCButton.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IButton;
+
+/**
+ * <p>
+ * Class that represents buttons in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCButton extends JFCGUIElement implements IButton {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCButton.
+     * </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 JFCButton(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Button";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCCanvas.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCCanvas.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCCanvas.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ICanvas;
+
+/**
+ * <p>
+ * Class that represents canvas' in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCCanvas extends JFCGUIElement implements ICanvas {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCCanvas.
+     * </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 JFCCanvas(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Canvas";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCCheckBox.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCCheckBox.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCCheckBox.java	(revision 922)
@@ -0,0 +1,52 @@
+// Module    : $RCSfile: JFCCheckBox.java,v $
+// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 03.09.2012 $
+// Project   : quest-plugin-jfc
+// Creation  : 2012 by pharms
+// Copyright : Patrick Harms, 2012
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox;
+
+/**
+ * <p>
+ * Class that represents check boxes in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCCheckBox extends JFCGUIElement implements ICheckBox {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCCheckBox.
+     * </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 JFCCheckBox(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "CheckBox";
+    }
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCComboBox.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCComboBox.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCComboBox.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IComboBox;
+
+/**
+ * <p>
+ * Class that represents combo boxes in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCComboBox extends JFCGUIElement implements IComboBox {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCComboBox.
+     * </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 JFCComboBox(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "ComboBox";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCDialog.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCDialog.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCDialog.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IFrame;
+
+/**
+ * <p>
+ * Class that represents dialogs in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCDialog extends JFCGUIElement implements IFrame {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCDialog.
+     * </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 JFCDialog(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Dialog";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCFrame.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCFrame.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCFrame.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IFrame;
+
+/**
+ * <p>
+ * Class that represents frames in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCFrame extends JFCGUIElement implements IFrame {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCFrame.
+     * </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 JFCFrame(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Frame";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCGUIElement.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCGUIElement.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCGUIElement.java	(revision 922)
@@ -0,0 +1,164 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.AbstractDefaultGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
+
+/**
+ * <p>
+ * Base class for all JFC GUI elements.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCGUIElement extends AbstractDefaultGUIElement {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Specification of the GUI Element
+     * </p>
+     */
+    private JFCGUIElementSpec specification;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCGUIElement.
+     * </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 JFCGUIElement(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+        this.specification = specification;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getPlatform()
+     */
+    @Override
+    public String getPlatform() {
+        return "JFC";
+    }
+
+    /**
+     * <p>
+     * Returns the type of the GUI element, i.e., the name of its Java class.
+     * </p>
+     * 
+     * @return the Java class name
+     */
+    public String getJavaType() {
+        return specification.getType();
+    }
+
+    /**
+     * <p>
+     * Returns the name of the GUI element.
+     * </p>
+     * 
+     * @return the name
+     */
+    String getName() {
+        return specification.getName();
+    }
+
+    /**
+     * <p>
+     * Returns the icon of the GUI element.
+     * </p>
+     * 
+     * @return the icon
+     */
+    String getIcon() {
+        return specification.getIcon();
+    }
+
+    /**
+     * <p>
+     * Returns the index of the GUI element.
+     * </p>
+     * 
+     * @return the index
+     */
+    int getIndex() {
+        return specification.getIndex();
+    }
+
+    /**
+     * <p>
+     * Returns the object hash of the GUI element.
+     * </p>
+     * 
+     * @return the object hash
+     */
+    int getElementHash() {
+        return specification.getElementHash();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement#updateSpecification(de.ugoe.cs.autoquest.eventcore
+     * .guimodel.IGUIElementSpec)
+     */
+    @Override
+    public void updateSpecification(IGUIElementSpec updateSpecification) {
+        if (updateSpecification instanceof JFCGUIElementSpec) {
+            specification.update(((JFCGUIElementSpec) updateSpecification));
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @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() {
+        String str =
+            getElementDescriptor() + "(" + getName() + "," + getElementHash() + "," + getIcon() +
+                "," + getIndex() + ")";
+        return str;
+    }
+
+    /**
+     * <p>
+     * A short string describing the GUI element, e.g., Button, Canvas, or ScrollBar.
+     * </p>
+     * 
+     * @return short element descriptor
+     */
+    protected String getElementDescriptor() {
+        return "Default";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCGUIElementSpec.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCGUIElementSpec.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCGUIElementSpec.java	(revision 922)
@@ -0,0 +1,392 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.collections15.CollectionUtils;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
+
+/**
+ * <p>
+ * Implements the specification of {@link IGUIElement} for {@link JFCGUIElement}s.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCGUIElementSpec implements IGUIElementSpec {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Current name of the GUI element
+     * </p>
+     */
+    private String name;
+
+    /**
+     * <p>
+     * Previous names of the GUI element as it may have changed over time.
+     * </p>
+     */
+    private List<String> formerNames = new ArrayList<String>();
+
+    /**
+     * <p>
+     * Type of the GUI element, i.e., its Java class.
+     * </p>
+     */
+    private String type = null;
+
+    /**
+     * <p>
+     * Icon associated with the GUI element.
+     * </p>
+     */
+    private String icon = null;
+
+    /**
+     * <p>
+     * Index of the GUI element in its parent element.
+     * </p>
+     */
+    private int index = -1;
+
+    /**
+     * <p>
+     * Hash code of the window element. Used as unique identifier during its existence.
+     * </p>
+     */
+    private int elementHash = -1;
+
+    /**
+     * <p>
+     * Previous hashes of the window as the window may have been destroyed and recreated.
+     * </p>
+     */
+    private List<Integer> formerElementHashes = new ArrayList<Integer>();
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec#getSecificationSimilarity(IGUIElementSpec
+     * )
+     */
+    @Override
+    public boolean getSimilarity(IGUIElementSpec other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof JFCGUIElementSpec)) {
+            return false;
+        }
+
+        JFCGUIElementSpec otherSpec = (JFCGUIElementSpec) other;
+
+        if ((type != otherSpec.type) && ((type != null) && (!type.equals(otherSpec.type)))) {
+            return false;
+        }
+
+        if ((icon != otherSpec.icon) && ((icon != null) && (!icon.equals(otherSpec.icon)))) {
+            return false;
+        }
+
+        // up to now, we compared, if the basics match. Now lets compare the id, the name and the
+        // index. All 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. The index may change, if later in a panel
+        // a new element is added or another one is removed. If the element hash or the name stay
+        // the same, then similarity is given. Therefore these are the first two comparisons
+
+        if (elementHash == otherSpec.elementHash) {
+            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 id and the name did not stay the same, then the name should be checked first.
+        // One of all known names of one of the specs must be equal to one of the known names of the
+        // respective other spec for similarity. Furthermore, if this is given, the index should
+        // have stayed the same.
+
+        if ((otherSpec.name != null) && formerNames.contains(otherSpec.name)) {
+            return index == otherSpec.index;
+        }
+
+        if ((name != null) && otherSpec.formerNames.contains(name)) {
+            return index == otherSpec.index;
+        }
+
+        if (CollectionUtils.containsAny(formerNames, otherSpec.formerNames)) {
+            return index == otherSpec.index;
+        }
+
+        // ok. Even the names do not match. This is usually a clear indication, that the elements
+        // are distinct. However, we check, if the former ids 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.
+        // Again we are restrictive and request the index to be equal as well.
+
+        if (formerElementHashes.contains(otherSpec.elementHash)) {
+            return index == otherSpec.index;
+        }
+
+        if (otherSpec.formerElementHashes.contains(elementHash)) {
+            return index == otherSpec.index;
+        }
+
+        if (CollectionUtils.containsAny(formerElementHashes, otherSpec.formerElementHashes)) {
+            return index == otherSpec.index;
+        }
+
+        // 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 JFCGUIElementSpec)) {
+            return false;
+        }
+
+        JFCGUIElementSpec otherSpec = (JFCGUIElementSpec) other;
+
+        return ((name == otherSpec.name) || ((name != null) && (name.equals(otherSpec.name)))) &&
+            ((type == otherSpec.type) || ((type != null) && (type.equals(otherSpec.type)))) &&
+            ((icon == otherSpec.icon) || ((icon != null) && (icon.equals(otherSpec.icon)))) &&
+            (index == otherSpec.index) && (elementHash == otherSpec.elementHash);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return (name + type + icon + index + elementHash).hashCode();
+    }
+
+    /**
+     * <p>
+     * Returns the name of the specified GUI element.
+     * </p>
+     * 
+     * @return the name
+     */
+    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 title of the specified GUI element.
+     * </p>
+     * 
+     * @return the title
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * <p>
+     * Returns the icon associated with the specified GUI element.
+     * </p>
+     * 
+     * @return the icon
+     */
+    public String getIcon() {
+        return icon;
+    }
+
+    /**
+     * <p>
+     * Returns the index of the specified GUI element in its parent element.
+     * </p>
+     * 
+     * @return the index
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * <p>
+     * Returns the object hash of the specified GUI element.
+     * </p>
+     * 
+     * @return the elementHash
+     */
+    public int getElementHash() {
+        return elementHash;
+    }
+
+    /**
+     * <p>
+     * Sets the name of the specified GUI element.
+     * </p>
+     * 
+     * @param newName
+     *            the name
+     */
+    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 type of the specified GUI element.
+     * </p>
+     * 
+     * @param type
+     *            the type
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     * <p>
+     * Sets the icon associated with the specified GUI element.
+     * </p>
+     * 
+     * @param icon
+     *            the icon
+     */
+    public void setIcon(String icon) {
+        this.icon = icon;
+    }
+
+    /**
+     * <p>
+     * Sets the index in its parent element of the specified GUI element.
+     * </p>
+     * 
+     * @param index
+     *            the index
+     */
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    /**
+     * <p>
+     * Sets the object hash of the specified GUI element.
+     * </p>
+     * 
+     * @param newElementHash
+     *            the element hash
+     */
+    public void setElementHash(int newElementHash) {
+        if ((this.elementHash > -1) && !this.formerElementHashes.contains(this.elementHash)) {
+            this.formerElementHashes.add(this.elementHash);
+        }
+
+        this.elementHash = newElementHash;
+    }
+
+    /**
+     * <p>
+     * Updates the specification with another specification.
+     * </p>
+     * 
+     * @param furtherSpec
+     *            specification used to update the current specification
+     */
+    void update(JFCGUIElementSpec other) {
+        if (other != this) {
+            for (int formerElementHash : other.formerElementHashes) {
+                setElementHash(formerElementHash);
+            }
+
+            if (elementHash != other.elementHash) {
+                elementHash = other.elementHash;
+            }
+
+            for (String formerName : other.formerNames) {
+                setName(formerName);
+            }
+
+            if ((name != other.name) && (name != null) && (!name.equals(other.name))) {
+                setName(other.name);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "[" + getName() + ";\"" + type + "\";\"" + icon + "\";" + index + ";" + elementHash +
+            "]";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCListBox.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCListBox.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCListBox.java	(revision 922)
@@ -0,0 +1,52 @@
+// Module    : $RCSfile: JFCListBox.java,v $
+// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 03.09.2012 $
+// Project   : quest-plugin-jfc
+// Creation  : 2012 by pharms
+// Copyright : Patrick Harms, 2012
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IListBox;
+
+/**
+ * <p>
+ * Class that represents list boxes in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCListBox extends JFCGUIElement implements IListBox {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCListBox.
+     * </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 JFCListBox(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "List";
+    }
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenu.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenu.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenu.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IMenu;
+
+/**
+ * <p>
+ * Class that represents menus in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCMenu extends JFCGUIElement implements IMenu {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCMenu.
+     * </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 JFCMenu(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Menu";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenuBar.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenuBar.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenuBar.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IMenuBar;
+
+/**
+ * <p>
+ * Class that menu bar buttons in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCMenuBar extends JFCMenu implements IMenuBar {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCMenuBar.
+     * </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 JFCMenuBar(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCMenu#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "MenuBar";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenuButton.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenuButton.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCMenuButton.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IMenuButton;
+
+/**
+ * <p>
+ * Class that represents menu buttons in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCMenuButton extends JFCButton implements IMenuButton {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCMenuButton.
+     * </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 JFCMenuButton(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCButton#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "MenuButton";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCPanel.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCPanel.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCPanel.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IPanel;
+
+/**
+ * <p>
+ * Class that represents panels in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCPanel extends JFCGUIElement implements IPanel {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCPanel.
+     * </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 JFCPanel(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Panel";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCRadioButton.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCRadioButton.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCRadioButton.java	(revision 922)
@@ -0,0 +1,52 @@
+// Module    : $RCSfile: JFCRadioButton.java,v $
+// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 03.09.2012 $
+// Project   : quest-plugin-jfc
+// Creation  : 2012 by pharms
+// Copyright : Patrick Harms, 2012
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IRadioButton;
+
+/**
+ * <p>
+ * Class that represents radio buttons in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCRadioButton extends JFCGUIElement implements IRadioButton {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCRadioButton.
+     * </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 JFCRadioButton(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "RadioButton";
+    }
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCScrollBar.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCScrollBar.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCScrollBar.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IScrollBar;
+
+/**
+ * <p>
+ * Class that represents scroll bars in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCScrollBar extends JFCGUIElement implements IScrollBar {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCScrollBar.
+     * </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 JFCScrollBar(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "ScrollBar";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCScrollPane.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCScrollPane.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCScrollPane.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IScrollPane;
+
+/**
+ * <p>
+ * Class that represents scroll panes in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCScrollPane extends JFCGUIElement implements IScrollPane {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCScrollPane.
+     * </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 JFCScrollPane(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "ScrollPane";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCShape.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCShape.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCShape.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IShape;
+
+/**
+ * <p>
+ * Class that represents shapes in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCShape extends JFCGUIElement implements IShape {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCShape.
+     * </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 JFCShape(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Shape";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCSplitPane.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCSplitPane.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCSplitPane.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ISplitPane;
+
+/**
+ * <p>
+ * Class that represents split panes in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCSplitPane extends JFCGUIElement implements ISplitPane {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCSplitPane.
+     * </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 JFCSplitPane(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "SplitPane";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTabbedPane.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTabbedPane.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTabbedPane.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITabbedPane;
+
+/**
+ * <p>
+ * Class that represents tabbed panes in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCTabbedPane extends JFCGUIElement implements ITabbedPane {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCTabbedPane.
+     * </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 JFCTabbedPane(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "TabbedPane";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTable.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTable.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTable.java	(revision 922)
@@ -0,0 +1,52 @@
+// Module    : $RCSfile: JFCTable.java,v $
+// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 03.09.2012 $
+// Project   : quest-plugin-jfc
+// Creation  : 2012 by pharms
+// Copyright : Patrick Harms, 2012
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITable;
+
+/**
+ * <p>
+ * Class that represents tables in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCTable extends JFCGUIElement implements ITable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCTable.
+     * </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 JFCTable(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Table";
+    }
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTextArea.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTextArea.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTextArea.java	(revision 922)
@@ -0,0 +1,47 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
+
+/**
+ * <p>
+ * Class that represents text areas in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCTextArea extends JFCGUIElement implements ITextField {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCTextArea.
+     * </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 JFCTextArea(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "TextArea";
+    }
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTextField.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTextField.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTextField.java	(revision 922)
@@ -0,0 +1,47 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
+
+/**
+ * <p>
+ * Class that represents text fields in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCTextField extends JFCGUIElement implements ITextField {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCTextField.
+     * </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 JFCTextField(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "TextField";
+    }
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCToolBar.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCToolBar.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCToolBar.java	(revision 922)
@@ -0,0 +1,48 @@
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.IToolBar;
+
+/**
+ * <p>
+ * Class that represents toolbars in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCToolBar extends JFCGUIElement implements IToolBar {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCToolBar.
+     * </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 JFCToolBar(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "ToolBar";
+    }
+
+}
Index: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTree.java
===================================================================
--- trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTree.java	(revision 922)
+++ trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/guimodel/JFCTree.java	(revision 922)
@@ -0,0 +1,52 @@
+// Module    : $RCSfile: JFCTree.java,v $
+// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 03.09.2012 $
+// Project   : quest-plugin-jfc
+// Creation  : 2012 by pharms
+// Copyright : Patrick Harms, 2012
+
+package de.ugoe.cs.autoquest.plugin.jfc.guimodel;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITree;
+
+/**
+ * <p>
+ * Class that represents trees in JFC GUIs.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Patrick Harms
+ */
+public class JFCTree extends JFCGUIElement implements ITree {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Constructor. Creates a new JFCTree.
+     * </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 JFCTree(JFCGUIElementSpec specification, JFCGUIElement parent) {
+        super(specification, parent);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement#getElementDescriptor()
+     */
+    @Override
+    protected String getElementDescriptor() {
+        return "Tree";
+    }
+}
Index: trunk/autoquest-plugin-mfc-test/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-plugin-mfc-test/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-plugin-mfc-test/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-plugin-mfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerationRuleTest.java
===================================================================
--- trunk/autoquest-plugin-mfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerationRuleTest.java	(revision 922)
+++ trunk/autoquest-plugin-mfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/mfc/EventGenerationRuleTest.java	(revision 922)
@@ -0,0 +1,440 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.Namespace;
+import org.jdom.input.SAXBuilder;
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.plugin.mfc.EventGenerationRule;
+import de.ugoe.cs.autoquest.plugin.mfc.EventGenerationRule.Term;
+import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessageType;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @version $Revision$ $Date: 23.08.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class EventGenerationRuleTest {
+
+    /**
+     * 
+     */
+    Namespace rulesNamespace = null;
+    
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     */
+    @Test
+    public void testRule1() {
+        String ruleXML =
+            "<rule name='LeftClickButton'>\n" +
+            "  <msg type='&WM_LBUTTONDOWN;'>\n" +
+            "    <store var='clicked'/>\n" +
+            "  </msg>\n" +
+            "  <msg type='&WM_LBUTTONUP;'>\n" +
+            "    <equals>\n" +
+            "      <constValue value='Button'/>\n" +
+            "      <winInfoValue obj='this' winParam='class'/>\n" +
+            "    </equals>\n" +
+            "    <equals>\n" +
+            "      <paramValue obj='clicked' param='window.hwnd'/>\n" +
+            "      <paramValue obj='this' param='window.hwnd'/>\n" +
+            "    </equals>\n" +
+            "  </msg>\n" +
+            "  <genMsg delay='500'>\n" +
+            "    <type>\n" +
+            "      <constValue value='&BM_CLICK;'/>\n" +
+            "    </type>\n" +
+            "    <target>\n" +
+            "      <msgInfoValue obj='clicked' msgParam='target'/>\n" +
+            "    </target>\n" +
+            "  </genMsg>\n" +
+            "</rule>";
+        
+        Element ruleElement = getRuleElement(ruleXML);
+        
+        EventGenerationRule rule = new EventGenerationRule(ruleElement, rulesNamespace);
+        
+        assertEquals("LeftClickButton", rule.getName());
+        
+        assertNotNull(rule.getMessageConditions());
+        assertEquals(2, rule.getMessageConditions().size());
+        
+        // check condition 1
+        EventGenerationRule.MessageCondition condition1 = rule.getMessageConditions().get(0);
+        assertEquals(WindowsMessageType.WM_LBUTTONDOWN, condition1.getMessageType());
+        assertFalse(condition1.matchMultiple());
+        assertNotNull(condition1.getAttributeConditions());
+        assertEquals(0, condition1.getAttributeConditions().size());
+        assertNotNull(condition1.getMessagesToStore());
+        assertEquals(1, condition1.getMessagesToStore().size());
+        
+        assertTerm(condition1.getMessagesToStore().get(0), "store", "clicked", null, null,
+                   null, null, null, null, null, null);
+
+        // check condition 2
+        EventGenerationRule.MessageCondition condition2 = rule.getMessageConditions().get(1);
+        assertEquals(WindowsMessageType.WM_LBUTTONUP, condition2.getMessageType());
+        assertFalse(condition2.matchMultiple());
+        assertNotNull(condition2.getAttributeConditions());
+        assertEquals(2, condition2.getAttributeConditions().size());
+        assertNotNull(condition2.getMessagesToStore());
+        assertEquals(0, condition2.getMessagesToStore().size());
+        
+        EventGenerationRule.AttributeCondition attrCond1 =
+            condition2.getAttributeConditions().get(0);
+        
+        assertTerm(attrCond1.getLeftHandSide(), "constValue", null, null, null,
+                   null, null, null, null, "Button", null);
+
+        assertTerm(attrCond1.getRightHandSide(), "winInfoValue", "this", null, null,
+                   null, null, null, null, null, "class");
+
+        EventGenerationRule.AttributeCondition attrCond2 =
+            condition2.getAttributeConditions().get(1);
+            
+        assertTerm(attrCond2.getLeftHandSide(), "paramValue", "clicked", null, "window.hwnd",
+                   null, null, null, null, null, null);
+
+        assertTerm(attrCond2.getRightHandSide(), "paramValue", "this", null, "window.hwnd",
+                   null, null, null, null, null, null);
+
+        // check event parameters
+        assertNotNull(rule.getEventParameters());
+        assertEquals(0, rule.getEventParameters().size());
+        
+        // check replay specs
+        assertNotNull(rule.getReplayMessageSpecifications());
+        assertEquals(1, rule.getReplayMessageSpecifications().size());
+        
+        // check replay spec 1
+        EventGenerationRule.ReplayMessageSpec replaySpec =
+            rule.getReplayMessageSpecifications().get(0);
+        
+        assertNotNull(replaySpec);
+        assertEquals(500, replaySpec.getDelay());
+        assertTrue(replaySpec.generateSingleMessage());
+        assertNull(replaySpec.getLparam());
+        assertNull(replaySpec.getLparamHiWord());
+        assertNull(replaySpec.getLparamLoWord());
+        assertNull(replaySpec.getReplayObjectId());
+        assertTerm(replaySpec.getTarget(), "msgInfoValue", "clicked", "target", null,
+                   null, null, null, null, null, null);
+        assertTerm(replaySpec.getType(), "constValue", null, null, null,
+                   null, null, null, null, "" + WindowsMessageType.BM_CLICK.getNumber(), null);
+        assertNull(replaySpec.getWparam());
+        assertNull(replaySpec.getWparamHiWord());
+        assertNull(replaySpec.getWparamLoWord());
+        
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     */
+    @Test
+    public void testRule2() {
+        
+        String ruleXML =
+            "<rule name='HScroll_TrackBar'>\n" +
+            "  <msg type='&WM_LBUTTONDOWN;'>\n" +
+            "    <equals>\n" +
+            "      <winInfoValue obj='this' winParam='class'/>\n" +
+            "      <constValue value='msctls_trackbar32'/>\n" +
+            "    </equals>\n" +
+            "    <store var='clicked'/>\n" +
+            "  </msg>\n" +
+            "  <msg type='&WM_HSCROLL;' multiple='true'>\n" +
+            "    <equals>\n" +
+            "      <paramValue obj='this' param='scrollBarHandle'/>\n" +
+            "      <paramValue obj='clicked' param='window.hwnd'/>\n" +
+            "    </equals>\n" +
+            "    <storeSeq varSeq='scrolls'>\n" +
+            "      <resolveHwnd param='scrollBarHandle' storeParam='scrollBarTarget'/>\n" +
+            "    </storeSeq>\n" +
+            "  </msg>\n" +
+            "  <msg type='&WM_LBUTTONUP;'>\n" +
+            "    <equals>\n" +
+            "      <paramValue obj='this' param='window.hwnd'/>\n" +
+            "      <paramValue obj='clicked' param='window.hwnd'/>\n" +
+            "    </equals>\n" +
+            "  </msg>\n" +
+            "  <genMsgSeq delay='50'>\n" +
+            "    <type>\n" +
+            "      <constValue value='&TBM_SETPOS;'/>\n" +
+            "    </type>\n" +
+            "    <target>\n" +
+            "      <seqValue seqObj='scrolls' param='scrollBarTarget'/>\n" +
+            "    </target>\n" +
+            "    <LPARAM>\n" +
+            "      <seqValue seqObj='scrolls' param='scrollPos'/>\n" +
+            "    </LPARAM>\n" +
+            "    <WPARAM>\n" +
+            "      <constValue value='1'/>\n" +
+            "    </WPARAM>\n" +
+            "  </genMsgSeq>\n" +
+            "</rule>\n";
+        
+        Element ruleElement = getRuleElement(ruleXML);
+        
+        EventGenerationRule rule = new EventGenerationRule(ruleElement, rulesNamespace);
+        
+        assertEquals("HScroll_TrackBar", rule.getName());
+        
+        assertNotNull(rule.getMessageConditions());
+        assertEquals(3, rule.getMessageConditions().size());
+        
+        // check condition 1
+        EventGenerationRule.MessageCondition condition1 = rule.getMessageConditions().get(0);
+        assertEquals(WindowsMessageType.WM_LBUTTONDOWN, condition1.getMessageType());
+        assertFalse(condition1.matchMultiple());
+        assertNotNull(condition1.getAttributeConditions());
+        assertEquals(1, condition1.getAttributeConditions().size());
+        EventGenerationRule.AttributeCondition attrCond1 =
+            condition1.getAttributeConditions().get(0);
+            
+        assertTerm(attrCond1.getLeftHandSide(), "winInfoValue", "this", null, null,
+                   null, null, null, null, null, "class");
+
+        assertTerm(attrCond1.getRightHandSide(), "constValue", null, null, null,
+                   null, null, null, null, "msctls_trackbar32", null);
+
+        assertNotNull(condition1.getMessagesToStore());
+        assertEquals(1, condition1.getMessagesToStore().size());
+        
+        assertTerm(condition1.getMessagesToStore().get(0), "store", "clicked", null, null,
+                   null, null, null, null, null, null);
+
+        // check condition 2
+        EventGenerationRule.MessageCondition condition2 = rule.getMessageConditions().get(1);
+        assertEquals(WindowsMessageType.WM_HSCROLL, condition2.getMessageType());
+        assertTrue(condition2.matchMultiple());
+        assertNotNull(condition2.getAttributeConditions());
+        assertEquals(1, condition2.getAttributeConditions().size());
+        
+        EventGenerationRule.AttributeCondition attrCond2 =
+                condition2.getAttributeConditions().get(0);
+        
+        assertTerm(attrCond2.getLeftHandSide(), "paramValue", "this", null, "scrollBarHandle",
+                   null, null, null, null, null, null);
+
+        assertTerm(attrCond2.getRightHandSide(), "paramValue", "clicked", null, "window.hwnd",
+                   null, null, null, null, null, null);
+
+
+        assertNotNull(condition2.getMessagesToStore());
+        assertEquals(1, condition2.getMessagesToStore().size());
+        
+        String[][] resolveHwnd = { { "scrollBarHandle",  "scrollBarTarget" } };
+        assertTerm(condition2.getMessagesToStore().get(0), "storeSeq", null, null, null,
+                   resolveHwnd, "scrolls", null, null, null, null);
+
+        // check condition 3
+        EventGenerationRule.MessageCondition condition3 = rule.getMessageConditions().get(2);
+        assertEquals(WindowsMessageType.WM_LBUTTONUP, condition3.getMessageType());
+        assertFalse(condition3.matchMultiple());
+        assertNotNull(condition3.getAttributeConditions());
+        assertEquals(1, condition3.getAttributeConditions().size());
+        assertNotNull(condition3.getMessagesToStore());
+        assertEquals(0, condition3.getMessagesToStore().size());
+        
+        EventGenerationRule.AttributeCondition attrCond3 =
+            condition3.getAttributeConditions().get(0);
+        
+        assertTerm(attrCond3.getLeftHandSide(), "paramValue", "this", null, "window.hwnd",
+                   null, null, null, null, null, null);
+
+        assertTerm(attrCond3.getRightHandSide(), "paramValue", "clicked", null, "window.hwnd",
+                   null, null, null, null, null, null);
+
+        // check event parameters
+        assertNotNull(rule.getEventParameters());
+        assertEquals(0, rule.getEventParameters().size());
+        
+        // check replay specs
+        assertNotNull(rule.getReplayMessageSpecifications());
+        assertEquals(1, rule.getReplayMessageSpecifications().size());
+        
+        // check replay spec 1
+        EventGenerationRule.ReplayMessageSpec replaySpec =
+            rule.getReplayMessageSpecifications().get(0);
+        
+        assertNotNull(replaySpec);
+        assertEquals(50, replaySpec.getDelay());
+        assertFalse(replaySpec.generateSingleMessage());
+        assertTerm(replaySpec.getLparam(), "seqValue", null, null, null,
+                   null, "scrolls", "scrollPos", null, null, null);
+        assertNull(replaySpec.getLparamHiWord());
+        assertNull(replaySpec.getLparamLoWord());
+        assertEquals("scrolls", replaySpec.getReplayObjectId());
+        assertTerm(replaySpec.getTarget(), "seqValue", null, null, null,
+                   null, "scrolls", "scrollBarTarget", null, null, null);
+        assertTerm(replaySpec.getType(), "constValue", null, null, null,
+                   null, null, null, null, "" + WindowsMessageType.TBM_SETPOS.getNumber(), null);
+        assertTerm(replaySpec.getWparam(), "constValue", null, null, null,
+                   null, null, null, null, "1", null);
+        assertNull(replaySpec.getWparamHiWord());
+        assertNull(replaySpec.getWparamLoWord());
+        
+    }
+    
+    /**
+     *
+     */
+    private void assertTerm(Term       term,
+                            String     name,
+                            String     messageId,
+                            String     messageInfoName,
+                            String     messageParameterName,
+                            String[][] resolveHandles,
+                            String     sequenceId,
+                            String     sequenceParameterName,
+                            String     storeParameterName,
+                            String     value,
+                            String     windowParameterName)
+    {
+        assertNotNull(term);
+        
+        if (name != null) {
+            assertEquals("name does not match", name, term.getName());
+        }
+        else {
+            assertNull("name is not null", term.getName());
+        }
+        
+        if (messageId != null) {
+            assertEquals("messageId does not match", messageId, term.getMessageId());
+        }
+        else {
+            assertNull("messageId is not null", term.getMessageId());
+        }
+        
+        if (messageInfoName != null) {
+            assertEquals
+                ("messageInfoName does not match", messageInfoName, term.getMessageInfoName());
+        }
+        else {
+            assertNull("messageInfoName is not null", term.getMessageInfoName());
+        }
+
+        if (messageParameterName != null) {
+            assertEquals("messageParameterName does not match", messageParameterName,
+                         term.getMessageParameterName());
+        }
+        else {
+            assertNull("messageParameterName is not null", term.getMessageParameterName());
+        }
+        
+        if (resolveHandles != null) {
+            assertEquals("resolveHandles does not match", resolveHandles.length,
+                         term.getResolveHandles().size());
+            
+            for (int i = 0; i < resolveHandles.length; i++) {
+                assertTerm(term.getResolveHandles().get(i), "resolveHwnd", null, null,
+                           resolveHandles[i][0], null, null, null, resolveHandles[i][1], null, null);
+            }
+        }
+        else {
+            assertNull("resolveHandles is not null", term.getResolveHandles());
+        }
+        
+        if (sequenceId != null) {
+            assertEquals("sequenceId does not match", sequenceId, term.getSequenceId());
+        }
+        else {
+            assertNull("sequenceId is not null", term.getSequenceId());
+        }
+        
+        if (sequenceParameterName != null) {
+            assertEquals("sequenceParameter does not match", sequenceParameterName,
+                         term.getSequenceParameterName());
+        }
+        else {
+            assertNull("sequenceParameter is not null", term.getSequenceParameterName());
+        }
+        
+        if (storeParameterName != null) {
+            assertEquals("storeParameterName does not match",
+                         storeParameterName, term.getStoreParameterName());
+        }
+        else {
+            assertNull("storeParameterName is not null", term.getStoreParameterName());
+        }
+        
+        if (value != null) {
+            assertEquals("value does not match", value, term.getValue());
+        }
+        else {
+            assertNull("value is not null", term.getValue());
+        }
+        
+        if (windowParameterName != null) {
+            assertEquals("windowParameterName does not match", windowParameterName,
+                         term.getWindowParameterName());
+        }
+        else {
+            assertNull("windowParameterName is not null", term.getWindowParameterName());
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param ruleXML
+     * @return
+     */
+    private Element getRuleElement(String ruleXML) {
+        String fullXML =
+            "<?xml version='1.0' encoding='utf-8'?>\n" +
+            "<!DOCTYPE rules SYSTEM 'data/ruleDoctype.dtd'>\n" +
+            "<rules xmlns='ul:rules' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
+            "                        xsi:schemaLocation='ul:rules ruleSchema.xsd'>\n";
+        
+        fullXML += ruleXML;
+        fullXML += "\n</rules>\n";
+
+        SAXBuilder builder = new SAXBuilder();
+        Document doc = null;
+
+        try {
+            ByteArrayInputStream in = new ByteArrayInputStream(fullXML.getBytes("UTF-8"));
+            doc = builder.build(in);
+            rulesNamespace = Namespace.getNamespace("ul:rules");
+        }
+        catch (JDOMException e) {
+            Console.printerrln("Invalid rules file.");
+            e.printStackTrace();
+        }
+        catch (IOException e) {
+            Console.printerrln("Invalid rules file.");
+            e.printStackTrace();
+        }
+
+        Element rulesRoot = doc.getRootElement();
+        
+        @SuppressWarnings("unchecked")
+        List<Element> ruleElements = rulesRoot.getChildren("rule", rulesNamespace);
+        
+        assertEquals(1, ruleElements.size());
+
+        return ruleElements.get(0);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/mfc/MFCLogParserTest.java
===================================================================
--- trunk/autoquest-plugin-mfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/mfc/MFCLogParserTest.java	(revision 922)
+++ trunk/autoquest-plugin-mfc-test/src/test/java/de/ugoe/cs/autoquest/plugin/mfc/MFCLogParserTest.java	(revision 922)
@@ -0,0 +1,180 @@
+package de.ugoe.cs.autoquest.plugin.mfc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.ugoe.cs.autoquest.commands.sequences.CMDgenerateReplayfile;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.plugin.mfc.LogPreprocessor;
+import de.ugoe.cs.autoquest.plugin.mfc.MFCLogParser;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+import de.ugoe.cs.util.console.listener.IOutputListener;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision$ $Date$
+ * @author 2011, last modified by $Author$
+ */
+public class MFCLogParserTest implements IOutputListener {
+
+    /** */
+    private File tmpFile;
+    
+    /** */
+    private PrintStream outputStream;
+
+    /**
+     * 
+     */
+    @Before
+    public void setUp() throws Exception {
+        outputStream = new PrintStream(new FileOutputStream("blub.log"));
+        Console.getInstance().registerOutputListener(this);
+        
+        tmpFile = new File("tmp.xml");
+        
+        if (tmpFile.exists()) {
+            assertTrue(tmpFile.delete());
+        }
+    }
+    
+    /**
+     * 
+     */
+    @After
+    public void tearDown() throws Exception {
+        if (!System.out.equals(outputStream) && !System.err.equals(outputStream)) {
+            outputStream.close();
+        }
+        
+        if (tmpFile.exists()) {
+            assertTrue(tmpFile.delete());
+        }
+    }
+    
+    
+    /**
+     *
+     */
+    @Test
+    public void testTrace() throws Exception {
+        File traceFile = new File(ClassLoader.getSystemResource("trace.txt").getFile());
+        
+        new LogPreprocessor().convertToXml(traceFile.getAbsolutePath(),
+                                           tmpFile.getAbsolutePath());
+        
+        MFCLogParser parser = new MFCLogParser();
+        parser.parseFile(tmpFile);
+        Collection<List<Event>> events = parser.getSequences();
+
+        assertNotNull(events);
+        assertTrue(events.size() > 0);
+
+        System.err.println("{");
+        for (List<Event> session : events) {
+            System.err.println("  {");
+            for (Event event : session) {
+                System.err.print("    ");
+                System.err.print(event);
+                System.err.println(",");
+            }
+            System.err.println("  }");
+        }
+        System.err.println("}");
+        System.err.println("\n\n");
+
+        GUIModel guiModel = parser.getGuiModel();
+        assertNotNull(guiModel);
+        assertNotSame(0, guiModel.getRootElements().size());
+
+        guiModel.dump(System.err, "UTF-8");
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testLogfile() throws Exception {
+        File logFile = new File(ClassLoader.getSystemResource("log.xml").getFile());
+
+        MFCLogParser parser = new MFCLogParser();
+        parser.parseFile(logFile);
+        Collection<List<Event>> events = parser.getSequences();
+
+        assertNotNull(events);
+        assertTrue(events.size() > 0);
+
+        System.err.println("{");
+        for (List<Event> session : events) {
+            System.err.println("  {");
+            for (Event event : session) {
+                System.err.print("    ");
+                System.err.print(event);
+                System.err.println(",");
+            }
+            System.err.println("  }");
+        }
+        System.err.println("}");
+        System.err.println("\n\n");
+
+        GUIModel guiModel = parser.getGuiModel();
+        assertNotNull(guiModel);
+
+        guiModel.dump(System.err, "UTF-8");
+
+        
+        GlobalDataContainer.getInstance().addData("seqs",  parser.getSequences());
+        
+        List<Object> parameters = new ArrayList<Object>();
+        parameters.add(tmpFile.getAbsolutePath());
+        parameters.add("seqs");
+        
+        CMDgenerateReplayfile cmd = new CMDgenerateReplayfile();
+        cmd.run(parameters);
+        
+        InputStreamReader reader1 = 
+            new InputStreamReader(ClassLoader.getSystemResourceAsStream("replay.xml"), "UTF-8");
+
+        InputStreamReader reader2 = new InputStreamReader(new FileInputStream(tmpFile), "UTF-8");
+        
+        try {
+            int sign1;
+            do {
+                sign1 = reader1.read();
+                assertEquals(sign1, reader2.read());
+            }
+            while (sign1 > -1);
+        }
+        finally {
+            reader1.close();
+            reader2.close();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.util.console.listener.IOutputListener#outputMsg(java.lang.String)
+     */
+    @Override
+    public void outputMsg(String newMessage) {
+        outputStream.println(newMessage);
+    }
+
+}
Index: trunk/autoquest-plugin-mfc/data/guimappings/guimapping-MFC.txt
===================================================================
--- trunk/autoquest-plugin-mfc/data/guimappings/guimapping-MFC.txt	(revision 921)
+++ trunk/autoquest-plugin-mfc/data/guimappings/guimapping-MFC.txt	(revision 922)
@@ -1,21 +1,21 @@
-Afx\: = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCWindow
-AfxFrameOrView90u = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCWindow
+Afx\: = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCWindow
+AfxFrameOrView90u = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCWindow
 
-MDIClient = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCPanel
-Button = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCButton
-Static = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCCanvas
-Edit = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCTextArea
-ComboBox = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCComboBox
-ComboLBox = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCComboBox
-ListBox = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCListBox
+MDIClient = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCPanel
+Button = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCButton
+Static = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCCanvas
+Edit = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCTextArea
+ComboBox = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCComboBox
+ComboLBox = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCComboBox
+ListBox = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCListBox
 
-SysTabControl32 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCTabbedPane
-SysListView32 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCListBox
-ComboBoxEx32 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCComboBox
-ToolbarWindow32 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCToolBar
-SysAnimate32 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCCanvas
+SysTabControl32 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCTabbedPane
+SysListView32 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCListBox
+ComboBoxEx32 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCComboBox
+ToolbarWindow32 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCToolBar
+SysAnimate32 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCCanvas
 
-\#32770 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCDialog
+\#32770 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCDialog
 
-msctls_statusbar32 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCPanel
-msctls_trackbar32 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCTrackBar
+msctls_statusbar32 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCPanel
+msctls_trackbar32 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCTrackBar
Index: trunk/autoquest-plugin-mfc/data/guimappings/guimapping-testcases.txt
===================================================================
--- trunk/autoquest-plugin-mfc/data/guimappings/guimapping-testcases.txt	(revision 921)
+++ trunk/autoquest-plugin-mfc/data/guimappings/guimapping-testcases.txt	(revision 922)
@@ -1,1 +1,1 @@
-ATL\:0048A4D0 = de.ugoe.cs.quest.plugin.mfc.guimodel.MFCDialog
+ATL\:0048A4D0 = de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCDialog
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;
+    }
+
+}
Index: trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/PHPPlugin.java
===================================================================
--- trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/PHPPlugin.java	(revision 922)
+++ trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/PHPPlugin.java	(revision 922)
@@ -0,0 +1,45 @@
+package de.ugoe.cs.autoquest.plugin.php;
+
+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 PHP plug-in.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class PHPPlugin implements QuestPlugin {
+
+	/**
+	 * <p>
+	 * The command packages of this plug-in.
+	 * </p>
+	 */
+	private final static String[] commandPackages = new String[] { "de.ugoe.cs.autoquest.plugin.php.commands" };
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.plugin.QuestPlugin#getTitle()
+	 */
+	@Override
+	public String getTitle() {
+		return "GUITAR-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-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/WeblogParser.java
===================================================================
--- trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/WeblogParser.java	(revision 922)
+++ trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/WeblogParser.java	(revision 922)
@@ -0,0 +1,507 @@
+package de.ugoe.cs.autoquest.plugin.php;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.plugin.php.eventcore.PHPEventTarget;
+import de.ugoe.cs.autoquest.plugin.php.eventcore.PHPEventType;
+import de.ugoe.cs.autoquest.plugin.php.eventcore.WebRequest;
+import de.ugoe.cs.util.FileTools;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Provides functionality to parse log files with web request.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WeblogParser {
+
+	/**
+	 * <p>
+	 * Timeout between two sessions in milliseconds.
+	 * </p>
+	 */
+	private long timeout;
+
+	/**
+	 * <p>
+	 * Minimal length of a session. All shorter sessions will be pruned.<br>
+	 * Default: 2
+	 * </p>
+	 */
+	private int minLength = 2;
+
+	/**
+	 * <p>
+	 * Maximal length of a session. All longer sessions will be pruned.<br>
+	 * Default: 100
+	 * </p>
+	 */
+	private int maxLength = 100;
+
+	/**
+	 * <p>
+	 * URL of the server that generated the log that is currently parser; null
+	 * of URL is not available.<br>
+	 * Default: null
+	 * </p>
+	 */
+	private String url = null;
+
+	/**
+	 * <p>
+	 * Collection of generated sequences.
+	 * </p>
+	 */
+	private List<List<Event>> sequences;
+
+	/**
+	 * <p>
+	 * List that stores the users (identified through their cookie id) to each
+	 * sequence.
+	 * </p>
+	 */
+	private List<String> users;
+
+	/**
+	 * <p>
+	 * List that stores the frequent users (identified through their cookie id)
+	 * to each sequence.
+	 * </p>
+	 */
+	private List<String> frequentUsers;
+
+	/**
+	 * <p>
+	 * Sequences for all frequent users.
+	 * </p>
+	 */
+	private List<Collection<List<Event>>> sequencesFrequentUsers;
+
+	/**
+	 * <p>
+	 * Threshold that defines how many sessions of a user are require to deem
+	 * the user frequent. Note, that only sessions whose lengths is in range if
+	 * {@link #minLength} and {@link #maxLength} are counted.
+	 * </p>
+	 */
+	private int frequentUsersThreshold = -1;
+
+	/**
+	 * <p>
+	 * Name and path of the robot filter.
+	 * </p>
+	 */
+	private static final String ROBOTFILTERFILE = "misc/robotfilter.txt";
+
+	/**
+	 * <p>
+	 * Field that contains a regular expression that matches all robots
+	 * contained in {@link #ROBOTFILTERFILE}.
+	 * </p>
+	 */
+	private String robotRegex = null;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new WeblogParser with a default timeout of
+	 * 3,600,000 milliseconds (1 hour).
+	 * </p>
+	 */
+	public WeblogParser() {
+		this(3600000);
+	}
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new WeblogParser.
+	 * </p>
+	 * 
+	 * @param timeout
+	 *            session timeout
+	 */
+	public WeblogParser(long timeout) {
+		this.timeout = timeout;
+	}
+
+	/**
+	 * <p>
+	 * Returns the generated event sequences.
+	 * </p>
+	 * 
+	 * @return generated event sequences
+	 */
+	public Collection<List<Event>> getSequences() {
+		return sequences;
+	}
+
+	/**
+	 * <p>
+	 * Sets the session timeout.
+	 * </p>
+	 * 
+	 * @param timeout
+	 *            new session timeout
+	 */
+	public void setTimeout(long timeout) {
+		this.timeout = timeout;
+	}
+
+	/**
+	 * <p>
+	 * Sets the minimal length of a session. All sessions that contain less
+	 * events will be pruned.
+	 * </p>
+	 * 
+	 * @param minLength
+	 *            new minimal length
+	 */
+	public void setMinLength(int minLength) {
+		this.minLength = minLength;
+	}
+
+	/**
+	 * <p>
+	 * Sets the maximal length of a session. All sessions that contain more
+	 * events will be pruned.
+	 * </p>
+	 * 
+	 * @param maxLength
+	 *            new maximal length
+	 */
+	public void setMaxLength(int maxLength) {
+		this.maxLength = maxLength;
+	}
+
+	/**
+	 * <p>
+	 * Sets the URL of the server from which this log was generated. Often
+	 * required for replay generation
+	 * </p>
+	 * 
+	 * @param url
+	 *            URL of the server
+	 */
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	/**
+	 * <p>
+	 * Sets the threshold for frequent users.
+	 * </p>
+	 * 
+	 * @param threshold
+	 *            threshold value; if the value is &lt;1, the sessions of the
+	 *            frequent users will not be determined
+	 */
+	public void setFrequentUserThreshold(int threshold) {
+		this.frequentUsersThreshold = threshold;
+	}
+
+	/**
+	 * <p>
+	 * Returns the IDs of all frequent users.
+	 * </p>
+	 * 
+	 * @return IDs of the frequent users
+	 */
+	public List<String> getFrequentUsers() {
+		return frequentUsers;
+	}
+
+	/**
+	 * <p>
+	 * Returns the sequences of all frequent users.
+	 * </p>
+	 * </p>
+	 * 
+	 * @return list of the sequences of all frequent users
+	 */
+	public List<Collection<List<Event>>> getFrequentUserSequences() {
+		return sequencesFrequentUsers;
+	}
+
+	/**
+	 * <p>
+	 * Parses a web log file.
+	 * </p>
+	 * 
+	 * @param filename
+	 *            name and path of the log file
+	 * @throws IOException
+	 *             thrown if there is a problem with reading the log file
+	 * @throws FileNotFoundException
+	 *             thrown if the log file is not found
+	 * @throws ParseException
+	 *             thrown the date format is invalid
+	 */
+	public void parseFile(String filename) throws IOException,
+			FileNotFoundException, ParseException {
+		String[] lines = FileTools.getLinesFromFile(filename);
+
+		Map<String, List<Integer>> cookieSessionMap = new HashMap<String, List<Integer>>();
+		Map<String, Long> cookieTimestampMap = new HashMap<String, Long>();
+		
+		int lastId = -1;
+
+		SimpleDateFormat dateFormat = new SimpleDateFormat(
+				"yyyy-MM-dd HH:mm:ss");
+		loadRobotRegex();
+
+		sequences = new ArrayList<List<Event>>();
+		users = new ArrayList<String>();
+
+		int lineCounter = 0;
+		for (String line : lines) {
+			lineCounter++;
+			String[] values = line.substring(1, line.length() - 1).split(
+					"\" \"");
+
+			// use cookie as session identifier
+			int cookieStart = values[0].lastIndexOf('.');
+			String cookie = values[0].substring(cookieStart + 1);
+			String dateString = values[1];
+			long timestamp = dateFormat.parse(dateString).getTime();
+			String uriString = values[2];
+			// String ref = values[3]; // referer is not yet used!
+			String agent;
+			if (values.length > 4) {
+				agent = values[4];
+			} else {
+				agent = "noagent";
+			}
+
+			List<String> postedVars = new ArrayList<String>();
+			if (values.length == 6) { // post vars found
+				for (String postVar : values[5].trim().split(" ")) {
+					if (!isBrokenVariable(postVar)) {
+						postedVars.add(postVar);
+					}
+				}
+			}
+			if (!isRobot(agent)) {
+				try {
+					URI uri = new URI(uriString);
+					String path = uri.getPath();
+					List<String> getVars = extractGetVarsFromUri(uri);
+					
+					IEventType type = new PHPEventType(path, postedVars, getVars);
+					IEventTarget target = new PHPEventTarget(path);
+					Event event = new Event(type, target);
+					event.addReplayable(new WebRequest(url, path, postedVars, getVars));
+
+					// find session and add event
+					List<Integer> sessionIds = cookieSessionMap.get(cookie);
+					if (sessionIds == null) {
+						sessionIds = new ArrayList<Integer>();
+						// start new session
+						sessionIds.add(++lastId);
+						cookieSessionMap.put(cookie, sessionIds);
+						sequences.add(new LinkedList<Event>());
+						users.add(cookie);
+					}
+					Integer lastSessionIndex = sessionIds
+							.get(sessionIds.size() - 1);
+					List<Event> lastSession = sequences
+							.get(lastSessionIndex);
+					long lastEventTime = timestamp;
+					if (!lastSession.isEmpty()) {
+						lastEventTime = cookieTimestampMap.get(cookie);
+					}
+					if (timestamp - lastEventTime > timeout) {
+						sessionIds.add(++lastId);
+						List<Event> newSession = new LinkedList<Event>();
+						newSession.add(event);
+						sequences.add(newSession);
+						users.add(cookie);
+					} else {
+						lastSession.add(event);
+					}
+					cookieTimestampMap.put(cookie, timestamp);
+				} catch (URISyntaxException e) {
+					Console.traceln(Level.FINE, "Ignored line " + lineCounter + ": "
+							+ e.getMessage());
+				}
+			}
+		}
+		Console.traceln(Level.INFO, "" + sequences.size() + " user sequences found");
+		pruneSequences();
+		Console.traceln(Level.INFO, "" + sequences.size()
+				+ " remaining after pruning of sequences shorter than "
+				+ minLength);
+		Set<String> uniqueUsers = new HashSet<String>(users);
+		Console.traceln(Level.INFO, "" + uniqueUsers.size() + " unique users");
+		if (frequentUsersThreshold > 0) {
+			generateFrequentUserSequences(uniqueUsers);
+		}
+	}
+
+	/**
+	 * <p>
+	 * Generates the frequent user sequences, according to the threshold
+	 * {@link #frequentUsersThreshold}.
+	 * </p>
+	 * 
+	 * @param uniqueUsers
+	 *            set with all user IDs
+	 */
+	private void generateFrequentUserSequences(Set<String> uniqueUsers) {
+		frequentUsers = new ArrayList<String>();
+		sequencesFrequentUsers = new ArrayList<Collection<List<Event>>>();
+		for (String user : uniqueUsers) {
+			List<String> tmp = new ArrayList<String>();
+			tmp.add(user);
+			List<String> usersCopy = new LinkedList<String>(users);
+			usersCopy.retainAll(tmp);
+			int size = usersCopy.size();
+			if (size >= frequentUsersThreshold) {
+				frequentUsers.add(user);
+				Collection<List<Event>> sequencesUser = new ArrayList<List<Event>>();
+				for (int i = 0; i < sequences.size(); i++) {
+					if (users.get(i).equals(user)) {
+						sequencesUser.add(sequences.get(i));
+					}
+				}
+				sequencesFrequentUsers.add(sequencesUser);
+
+			}
+		}
+		Console.traceln(Level.INFO, "" + frequentUsers.size() + " users with more than "
+				+ frequentUsersThreshold + " sequences");
+	}
+
+	/**
+	 * <p>
+	 * Prunes sequences shorter than {@link #minLength} and longer than
+	 * {@link #maxLength}.
+	 * </p>
+	 */
+	private void pruneSequences() {
+		int i = 0;
+		while (i < sequences.size()) {
+			if ((sequences.get(i).size() < minLength)
+					|| sequences.get(i).size() > maxLength) {
+				sequences.remove(i);
+				users.remove(i);
+			} else {
+				i++;
+			}
+		}
+
+	}
+
+	/**
+	 * <p>
+	 * Reads {@link #ROBOTFILTERFILE} and creates a regular expression that
+	 * matches all the robots defined in the file. The regular expression is
+	 * stored in the field {@link #robotRegex}.
+	 * </p>
+	 * 
+	 * @throws IOException
+	 *             thrown if there is a problem reading the robot filter
+	 * @throws FileNotFoundException
+	 *             thrown if the robot filter is not found
+	 */
+	private void loadRobotRegex() throws IOException, FileNotFoundException {
+		String[] lines = FileTools.getLinesFromFile(ROBOTFILTERFILE);
+		StringBuilder regex = new StringBuilder();
+		for (int i = 0; i < lines.length; i++) {
+			regex.append("(.*" + lines[i] + ".*)");
+			if (i != lines.length - 1) {
+				regex.append('|');
+			}
+		}
+		robotRegex = regex.toString();
+	}
+
+	/**
+	 * <p>
+	 * Checks whether an agent is a robot.
+	 * </p>
+	 * 
+	 * @param agent
+	 *            agent that is checked
+	 * @return true, if the agent is a robot; false otherwise
+	 */
+	private boolean isRobot(String agent) {
+		return agent.matches(robotRegex);
+	}
+
+	/**
+	 * <p>
+	 * Parses the URI and extracts the GET variables that have been passed.
+	 * </p>
+	 * 
+	 * @param uri
+	 *            URI that is parsed
+	 * @return a list with all GET variables
+	 * @throws URISyntaxException
+	 *             thrown if one of the variables seems to indicate that the
+	 *             request is a malicious attack on the web application
+	 */
+	private List<String> extractGetVarsFromUri(URI uri)
+			throws URISyntaxException {
+		List<String> getVars = new ArrayList<String>();
+		String query = uri.getQuery();
+		if (query != null) {
+			String[] paramPairs = query.split("&");
+			for (String paramPair : paramPairs) {
+				String[] paramSplit = paramPair.split("=");
+				if (!isBrokenVariable(paramSplit[0])) {
+					for (int i = 1; i < paramSplit.length; i++) {
+						checkForAttack(paramSplit[i]);
+					}
+					getVars.add(paramSplit[0]);
+				}
+			}
+		}
+		return getVars;
+	}
+
+	/**
+	 * <p>
+	 * Checks if a variable is broken.Currently, the check rather imprecise and
+	 * checks only if the term &quot;and&quot; is part of the variable name.
+	 * </p>
+	 * 
+	 * @param var
+	 *            variable that is checked
+	 * @return true if the variable is broken, false otherwise
+	 */
+	private boolean isBrokenVariable(String var) {
+		return var.contains("and");
+	}
+
+	/**
+	 * <p>
+	 * Checks if the variable name send with a request seems like an attack on the server.
+	 * </p>
+	 * @param value
+	 * @throws URISyntaxException
+	 */
+	private void checkForAttack(String value) throws URISyntaxException {
+		if (value.contains("UNION+") || value.contains("SELECT+")) {
+			throw new URISyntaxException(value, "possible injection attack");
+		}
+	}
+}
Index: trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/commands/CMDloadWebSequences.java
===================================================================
--- trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/commands/CMDloadWebSequences.java	(revision 922)
+++ trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/commands/CMDloadWebSequences.java	(revision 922)
@@ -0,0 +1,116 @@
+package de.ugoe.cs.autoquest.plugin.php.commands;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.ParseException;
+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.plugin.php.WeblogParser;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to load sessions from a web log.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDloadWebSequences implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		if (parameters.size() < 1) {
+			throw new IllegalArgumentException();
+		}
+		String source;
+		String sequencesName;
+		String serverUrl = null;
+		int timeout = -1;
+		int minLength = -1;
+		int maxLength = -1;
+		boolean generateFrequentUsers = false;
+		int frequentUserThreshold = 20;
+		try {
+			source = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			if (parameters.size() >= 3) {
+				serverUrl = (String) parameters.get(2);
+			}
+			if (parameters.size() >= 6) {
+				timeout = Integer.parseInt((String) parameters.get(3));
+				minLength = Integer.parseInt((String) parameters.get(4));
+				maxLength = Integer.parseInt((String) parameters.get(5));
+			}
+			if (parameters.size() >= 8) {
+				generateFrequentUsers = Boolean
+						.parseBoolean((String) parameters.get(6));
+				frequentUserThreshold = Integer.parseInt((String) parameters
+						.get(7));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		WeblogParser parser = new WeblogParser();
+		if (serverUrl != null) {
+			parser.setUrl(serverUrl);
+		}
+		if (timeout != -1) {
+			parser.setTimeout(timeout);
+			parser.setMinLength(minLength);
+			parser.setMaxLength(maxLength);
+		}
+		if (generateFrequentUsers) {
+			parser.setFrequentUserThreshold(frequentUserThreshold);
+		}
+		try {
+			parser.parseFile(source);
+		} catch (FileNotFoundException e) {
+			Console.printerrln(e.getMessage());
+		} catch (IOException e) {
+			Console.printerrln(e.getMessage());
+		} catch (ParseException e) {
+			Console.printerrln("Invalid format of date stamps.");
+			Console.printerrln(e.getMessage());
+		}
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName,
+				parser.getSequences())) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+		if (generateFrequentUsers) {
+			List<String> frequentUserIDs = parser.getFrequentUsers();
+			List<Collection<List<Event>>> frequentUserSessions = parser
+					.getFrequentUserSequences();
+			for (int i = 0; i < frequentUserIDs.size(); i++) {
+				String seqName = sequencesName + "_" + frequentUserIDs.get(i);
+				if (GlobalDataContainer.getInstance().addData(seqName,
+						frequentUserSessions.get(i))) {
+					CommandHelpers.dataOverwritten(seqName);
+				}
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "loadWebSequences <filename> <sequencesName> {<serverUrl>} {<timeout> <minSessionLength> <maxSessionLength>} {<generateFrequentUsers> <frequentUserThreshold>}";
+	}
+
+}
Index: trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/PHPEventTarget.java
===================================================================
--- trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/PHPEventTarget.java	(revision 922)
+++ trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/PHPEventTarget.java	(revision 922)
@@ -0,0 +1,99 @@
+
+package de.ugoe.cs.autoquest.plugin.php.eventcore;
+
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+
+/**
+ * <p>
+ * Event target for PHP web requests.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: Aug 16, 2012$
+ * @author 2012, last modified by $Author: sherbold$
+ */
+public class PHPEventTarget implements IEventTarget {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Path of the PHP request.
+     * </p>
+     */
+    private String path;
+
+    /**
+     * <p>
+     * Constructor. Creates a new PHP event target as the path of the request.
+     * </p>
+     * 
+     * @param path
+     *            path of the URI of the event
+     */
+    public PHPEventTarget(String path) {
+        this.path = path;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getPlatform()
+     */
+    @Override
+    public String getPlatform() {
+        return "PHP";
+    }
+
+    @Override
+    public String getStringIdentifier() {
+        return this.toString();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return path;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals()
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PHPEventTarget) {
+            if (path != null) {
+                return path.equals(((PHPEventTarget) obj).path);
+            }
+            else {
+                return ((PHPEventTarget) obj).path == null;
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        if (path != null) {
+            hash = path.hashCode();
+        }
+        return hash;
+    }
+
+}
Index: trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/PHPEventType.java
===================================================================
--- trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/PHPEventType.java	(revision 922)
+++ trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/PHPEventType.java	(revision 922)
@@ -0,0 +1,129 @@
+
+package de.ugoe.cs.autoquest.plugin.php.eventcore;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+
+/**
+ * <p>
+ * Event type for PHP web requests.
+ * </p>
+ * 
+ * @version $Revision: $ $Date: Aug 16, 2012$
+ * @author 2012, last modified by $Author: sherbold$
+ */
+public class PHPEventType implements IEventType {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * Path of the web request.
+     * </p>
+     */
+    private final String path;
+
+    /**
+     * <p>
+     * List of post variable names posted with the request.
+     * </p>
+     */
+    private final List<String> postVars;
+
+    /**
+     * <p>
+     * List of get variable names posted with the request.
+     * </p>
+     */
+    private final List<String> getVars;
+
+    /**
+     * <p>
+     * Constructor. Creates a new PHPEventType from a given path, a list of post variables, and a
+     * list of get variables.
+     * </p>
+     * 
+     * @param path
+     *            path of the URI of the event
+     * @param postVars
+     *            POST variables send with the event
+     * @param getVars
+     *            GET variables send with the event
+     */
+    public PHPEventType(String path, List<String> postVars, List<String> getVars) {
+        this.path = path;
+        this.postVars = postVars;
+        this.getVars = getVars;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventType#getName()
+     */
+    @Override
+    public String getName() {
+        return "PHPEventType";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        String str = path;
+        if (getVars != null && !getVars.isEmpty()) {
+            str += "+GET" + getVars.toString().replace(" ", "");
+        }
+        if (postVars != null && !postVars.isEmpty()) {
+            str += "+POST" + postVars.toString().replace(" ", "");
+        }
+        return str;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PHPEventType) {
+            PHPEventType other = (PHPEventType) obj;
+            return ((path == other.path) || (path != null && path.equals(other.path))) &&
+                ((postVars == other.postVars) || (postVars!=null && postVars.equals(other.postVars))) &&
+                ((getVars == other.getVars) || (getVars!=null && getVars.equals(other.getVars)));
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hash = 17;
+        int multiplier = 7;
+        if (path != null) {
+            hash = hash * multiplier + path.hashCode();
+        }
+        if (postVars != null) {
+            hash = hash * multiplier + postVars.hashCode();
+        }
+        if (getVars != null) {
+            hash = hash * multiplier + getVars.hashCode();
+        }
+        return hash;
+    }
+
+}
Index: trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/WebRequest.java
===================================================================
--- trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/WebRequest.java	(revision 922)
+++ trunk/autoquest-plugin-php/src/main/java/de/ugoe/cs/autoquest/plugin/php/eventcore/WebRequest.java	(revision 922)
@@ -0,0 +1,170 @@
+package de.ugoe.cs.autoquest.plugin.php.eventcore;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.autoquest.eventcore.IReplayable;
+
+/**
+ * <p>
+ * Contains all information related to a web request, i.e., the path, the POST variables and the GET
+ * variables. The generated replay are for the command line tool {@code curl}. The requests do not
+ * contain correct values for the POST and GET request. Instead, only the parameters that are part
+ * of the requests are added and the values of the parameters are DATA_$PARAMNAME$_DATA, where
+ * $PARAMNAME$ is the upper case string of the parameter name. This allows test data generators to
+ * insert concrete values, as EventBench does not include a test data generator for web software.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WebRequest implements IReplayable {
+
+    /**
+     * <p>
+     * Id for object serialization.
+     * </p>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * <p>
+     * POST variables of the web request.
+     * </p>
+     */
+    List<String> postVars;
+
+    /**
+     * <p>
+     * GET variables of the web request.
+     * </p>
+     */
+    List<String> getVars;
+
+    /**
+     * <p>
+     * URI of the web request.
+     * </p>
+     */
+    String targetUri;
+
+    /**
+     * <p>
+     * URL of the server.
+     * </p>
+     */
+    String serverUrl;
+
+    /**
+     * <p>
+     * Constructor. Creates a new WebRequest.
+     * </p>
+     * 
+     * @param uri
+     *            URI of the request
+     * @param postVars
+     *            POST variables of the request
+     * @param getVars
+     *            GET variables of the request
+     */
+    public WebRequest(String url, String uri, List<String> postVars, List<String> getVars) {
+        serverUrl = url;
+        targetUri = uri;
+        this.postVars = new ArrayList<String>(postVars); // defensive copy
+        this.getVars = new ArrayList<String>(getVars);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getReplay()
+     */
+    @Override
+    public String getReplay() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("curl");
+        if (!postVars.isEmpty()) {
+            boolean isFirstPost = true;
+            for (String postVar : postVars) {
+                if (isFirstPost) {
+                    builder.append(" --data \"");
+                    isFirstPost = false;
+                }
+                else {
+                    builder.append('&');
+                }
+                builder.append(postVar + "=DATA_" + postVar.toUpperCase() + "_DATA");
+            }
+            builder.append('\"');
+        }
+        builder.append(' ');
+        if (serverUrl != null) {
+            builder.append(serverUrl);
+        }
+        builder.append(targetUri);
+        if (!getVars.isEmpty()) {
+            boolean isFirstGet = true;
+            for (String getVar : getVars) {
+                if (isFirstGet) {
+                    builder.append('?');
+                    isFirstGet = false;
+                }
+                else {
+                    builder.append('&');
+                }
+                builder.append(getVar + "=DATA_" + getVar.toUpperCase() + "_DATA");
+            }
+        }
+        return builder.toString();
+    }
+
+    /**
+     * <p>
+     * Two {@link WebRequest}s are equal, if their {@link #targetUri}, {@link #postVars}, and
+     * {@link #getVars} are equal.
+     * </p>
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other instanceof WebRequest) {
+            return targetUri.equals(((WebRequest) other).targetUri) &&
+                postVars.equals(((WebRequest) other).postVars) &&
+                getVars.equals(((WebRequest) other).getVars);
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int multiplier = 17;
+        int hash = 42;
+
+        hash = multiplier * hash + targetUri.hashCode();
+        hash = multiplier * hash + postVars.hashCode();
+        hash = multiplier * hash + getVars.hashCode();
+
+        return hash;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IReplayable#getDecorator()
+     */
+    @Override
+    public IReplayDecorator getDecorator() {
+        return null;
+    }
+
+}
Index: trunk/autoquest-runner/src/main/assembly/bin.xml
===================================================================
--- trunk/autoquest-runner/src/main/assembly/bin.xml	(revision 921)
+++ trunk/autoquest-runner/src/main/assembly/bin.xml	(revision 922)
@@ -14,5 +14,5 @@
       <includes></includes>
       <excludes>
-        <exclude>de.ugoe.cs.quest:quest-runner</exclude>
+        <exclude>de.ugoe.cs.autoquest:quest-runner</exclude>
       </excludes>
       <outputDirectory>lib</outputDirectory>
@@ -20,5 +20,5 @@
     <dependencySet>
       <includes>
-          <include>de.ugoe.cs.quest:quest-runner</include>
+          <include>de.ugoe.cs.autoquest:quest-runner</include>
       </includes>
       <outputDirectory></outputDirectory>
Index: trunk/autoquest-runner/src/main/java/de/ugoe/cs/autoquest/log4j/Log4JLogger.java
===================================================================
--- trunk/autoquest-runner/src/main/java/de/ugoe/cs/autoquest/log4j/Log4JLogger.java	(revision 922)
+++ trunk/autoquest-runner/src/main/java/de/ugoe/cs/autoquest/log4j/Log4JLogger.java	(revision 922)
@@ -0,0 +1,128 @@
+
+package de.ugoe.cs.autoquest.log4j;
+
+import java.util.logging.Level;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.listener.ICommandListener;
+import de.ugoe.cs.util.console.listener.IErrorListener;
+import de.ugoe.cs.util.console.listener.IExceptionListener;
+import de.ugoe.cs.util.console.listener.ITraceListener;
+
+/**
+ * <p>
+ * Implements logging based on the log4j API.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class Log4JLogger implements IErrorListener, ITraceListener, IExceptionListener,
+    ICommandListener
+{
+
+    /**
+     * <p>
+     * Reference to the logger.
+     * </p>
+     */
+    Logger logger;
+
+    /**
+     * <p>
+     * This is the trace level according to the Java logger API. We use this instead of the log4j
+     * levels themselves for the logging.
+     * </p>
+     */
+    Level traceLevel;
+
+    /**
+     * <p>
+     * Constructor. Creates a new Log4JLogger and registers the implemented listener with the
+     * {@link Console}.
+     * </p>
+     * @param traceLevel tracing level
+     */
+    public Log4JLogger(Level traceLevel) {
+        PropertyConfigurator.configure("data/log4j.properties");
+        logger = Logger.getLogger("de.ugoe.cs.autoquest");
+        logger.setLevel(convertToLog4JLevel(traceLevel));
+        this.traceLevel = traceLevel;
+        Console.getInstance().registerErrorListener(this);
+        Console.getInstance().registerTraceListener(this);
+        Console.getInstance().registerExceptionListener(this);
+        Console.getInstance().registerCommandListener(this);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.listener.ICommandListener#commandNotification (java.lang.String)
+     */
+    @Override
+    public void commandNotification(String command) {
+        logger.info("Command executed: " + command);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.listener.IExceptionListener#printStacktrace(java
+     * .lang.Exception)
+     */
+    @Override
+    public void logException(Exception e) {
+        logger.error("", e);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.listener.ITraceListener#traceMsg(java.lang.String )
+     */
+    @Override
+    public void traceMsg(String traceMessage, Level level) {
+        if( level.intValue()>=traceLevel.intValue()) {
+            logger.log(convertToLog4JLevel(level), traceMessage);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.listener.IErrorListener#errorMsg(java.lang.String )
+     */
+    @Override
+    public void errorMsg(String errMessage) {
+        logger.error(errMessage);
+    }
+
+    /**
+     * <p>
+     * Converts the log level described by {@link Level} into a {@link org.apache.log4j.Level}.
+     * </p>
+     * 
+     * @param level
+     *            java.util.logger.Level severity
+     * @return org.apache.log4j.Level severity
+     */
+    private org.apache.log4j.Level convertToLog4JLevel(Level level) {
+        if (level == Level.OFF) {
+            return org.apache.log4j.Level.OFF;
+        }
+        if (level == Level.SEVERE) {
+            return org.apache.log4j.Level.FATAL;
+        }
+        if (level == Level.WARNING) {
+            return org.apache.log4j.Level.WARN;
+        }
+        if (level == Level.INFO || level == Level.CONFIG) {
+            return org.apache.log4j.Level.INFO;
+        }
+        // remaining levels: FINE, FINER, FINEST, ALL
+        return org.apache.log4j.Level.ALL;
+    }
+}
Index: trunk/autoquest-runner/src/main/java/de/ugoe/cs/autoquest/ui/Runner.java
===================================================================
--- trunk/autoquest-runner/src/main/java/de/ugoe/cs/autoquest/ui/Runner.java	(revision 922)
+++ trunk/autoquest-runner/src/main/java/de/ugoe/cs/autoquest/ui/Runner.java	(revision 922)
@@ -0,0 +1,141 @@
+
+package de.ugoe.cs.autoquest.ui;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
+
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import de.ugoe.cs.autoquest.log4j.Log4JLogger;
+import de.ugoe.cs.autoquest.plugin.PluginLoader;
+import de.ugoe.cs.autoquest.plugin.QuestPlugin;
+import de.ugoe.cs.autoquest.ui.swt.MainWindow;
+import de.ugoe.cs.util.console.CommandExecuter;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.TextConsole;
+
+/**
+ * <p>
+ * Start-up class of the application.
+ * </p>
+ * <p>
+ * It sets up and starts the {@link Console}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class Runner {
+
+    public enum UITYPE {
+        text, swt
+    };
+    
+    public enum LEVELENUM {
+        OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL;
+        
+        public Level getLevel() {
+            switch (this)
+            {
+                case OFF:
+                    return Level.OFF;
+                case SEVERE:
+                    return Level.SEVERE;
+                case ALL:
+                    return Level.ALL;
+                case CONFIG:
+                    return Level.CONFIG;
+                case FINE:
+                    return Level.FINE;
+                case FINER:
+                    return Level.FINER;
+                case FINEST:
+                    return Level.FINEST;
+                case INFO:
+                    return Level.INFO;
+                case WARNING:
+                    return Level.WARNING;
+                default:
+                    throw new AssertionError("reached source code that should be unreachable");
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Main method of the application.
+     * </p>
+     * 
+     * @param args
+     *            if parameters are defined, they are interpreted as commands for the
+     *            {@link Console} and executed before the user can use the console; can be used to
+     *            perform batch operations
+     */
+    public static void main(String[] args) {
+        
+        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.misc");
+        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.sequences");
+        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.usability");
+        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.usage");
+        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.ui.swt.commands");
+
+        PluginLoader pluginLoader = new PluginLoader(new File("lib"));
+        pluginLoader.load();
+
+        for (QuestPlugin plugin : pluginLoader.getPlugins()) {
+            for (String commandPackage : plugin.getCommandPackages()) {
+                CommandExecuter.getInstance().addCommandPackage(commandPackage);
+            }
+        }
+
+        OptionParser parser = new OptionParser();
+        OptionSpec<LEVELENUM> log4j =
+            parser.accepts("log4j", "Allowed values: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL").withRequiredArg()
+                .ofType(LEVELENUM.class).defaultsTo(LEVELENUM.INFO);
+        OptionSpec<UITYPE> ui =
+            parser.accepts("ui", "Allowed values: text, swt").withRequiredArg()
+                .ofType(UITYPE.class).defaultsTo(UITYPE.text);
+        OptionSpec<LEVELENUM> trace =
+            parser.accepts("trace", "Allowed values: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL").withRequiredArg()
+                .ofType(LEVELENUM.class).defaultsTo(LEVELENUM.WARNING);
+        OptionSet options = parser.parse(args);
+
+        List<String> startupCommands = options.nonOptionArguments();
+        try {
+            if(options.valueOf(log4j)!=LEVELENUM.OFF) {
+                new Log4JLogger(options.valueOf(log4j).getLevel());
+            }
+
+            switch (options.valueOf(ui))
+            {
+                case text:
+                    TextConsole textConsole = new TextConsole(options.valueOf(trace).getLevel());
+                    for (String command : startupCommands) {
+                        CommandExecuter.getInstance().exec(command);
+                    }
+                    textConsole.run();
+                    break;
+                case swt:
+                    MainWindow mainWindow = new MainWindow(startupCommands, options.valueOf(trace).getLevel());
+                    mainWindow.open();
+                    break;
+                default:
+                    throw new AssertionError("reached source code that should be unreachable");
+            }
+        }
+        catch (OptionException e) {
+            System.err.println("Invalid Parameters: " + e.getMessage());
+            try {
+                parser.printHelpOn(System.out);
+            }
+            catch (IOException e1) {
+                // ignore exception.
+            }
+        }
+    }
+
+}
Index: trunk/autoquest-test-utils/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-test-utils/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-test-utils/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/tasktrees/TaskTreeChecker.java
===================================================================
--- trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/tasktrees/TaskTreeChecker.java	(revision 922)
+++ trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/tasktrees/TaskTreeChecker.java	(revision 922)
@@ -0,0 +1,456 @@
+package de.ugoe.cs.autoquest.tasktrees;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeInfo;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 01.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class TaskTreeChecker {
+    
+    /** */
+    private static Pattern taskPattern = Pattern.compile("([^{}]+)\\{|\\}");
+    
+    /** */
+    private static Pattern taskDetailsPattern =
+        Pattern.compile("\\s*(\\w*)\\s*(\\w*)\\s*((\\w*)|(\".*\"))?");
+    
+    /** */
+    private boolean doTrace;
+
+    /**
+     * TODO: comment
+     * 
+     */
+    public TaskTreeChecker() {
+        this(false);
+    }
+
+    /**
+     * TODO: comment
+     * 
+     */
+    public TaskTreeChecker(boolean doTrace) {
+        this.doTrace = doTrace;
+    }
+
+    /**
+     *
+     */
+    public void assertTaskTree(String taskTreeSpec, ITaskTree taskTree) {
+        Map<ITaskTreeNode, Integer> taskMapCopy = new HashMap<ITaskTreeNode, Integer>();
+
+        for (Map.Entry<ITaskTreeNode, ITaskTreeNodeInfo> entry : taskTree.getTaskMap().entrySet()) {
+            if (entry.getValue().getNoOfOccurencesInTree() > 0) {
+                taskMapCopy.put(entry.getKey(), entry.getValue().getNoOfOccurencesInTree());
+            }
+            else {
+                taskMapCopy.put(entry.getKey(), 1);
+            }
+        }
+
+        if (doTrace) {
+            dumpTaskMap(taskMapCopy);
+        }
+
+        TaskSpec task = null;
+
+        Matcher taskMatcher = taskPattern.matcher(taskTreeSpec);
+
+        while (taskMatcher.find()) {
+
+            task = parseTask(taskMatcher);
+            
+            if (task != null) {
+                assertTaskAndChildrenInMapAndRemove(task, taskMapCopy);
+            }
+        }
+
+        assertTrue("more tasks in map, than expected", taskMapCopy.isEmpty());
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param taskTree
+     */
+    public void dumpAsCheckString(ITaskTree taskTree) {
+        dumpNodeAsCheckString(taskTree.getRoot(), new int[4], "");
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param root
+     * @param string
+     */
+    private void dumpNodeAsCheckString(ITaskTreeNode node, int[] typeCounters, String indent) {
+        System.out.print("       \"");
+        System.out.print(indent);
+
+        if (node instanceof ISequence) {
+            System.out.print("Sequence sequence");
+            System.out.print(typeCounters[0]++);
+            System.out.println(" {\" +");
+        }
+        else if (node instanceof IIteration) {
+            System.out.print("Iteration iteration");
+            System.out.print(typeCounters[1]++);
+            System.out.println(" {\" +");
+        }
+        else if (node instanceof ISelection) {
+            System.out.print("Selection selection");
+            System.out.print(typeCounters[2]++);
+            System.out.println(" {\" +");
+        }
+        else if (node instanceof IEventTask) {
+            if (((IEventTask) node).getEventType() instanceof TextInput) {
+                System.out.print("TextInputEvent textInput");
+                System.out.print(typeCounters[3]++);
+                System.out.print(" \"");
+                System.out.print(((TextInput) ((IEventTask) node).getEventType()).getEnteredText());
+                System.out.print("\"");
+            }
+            else {
+                System.out.print("Event ");
+                System.out.print(((IEventTask) node).getEventType().getName());
+            }
+            System.out.print(" {}\" +");
+        }
+        else {
+            fail("unknown type of node in task tree " + node);
+        }
+
+        for (ITaskTreeNode child : node.getChildren()) {
+            dumpNodeAsCheckString(child, typeCounters, indent + "  ");
+        }
+
+        if (!(node instanceof IEventTask)) {
+            System.out.print("       \"");
+            System.out.print(indent);
+            System.out.print("}\" +");
+        }
+
+        System.out.println();
+    }
+
+    /**
+     * TODO: comment
+     * 
+     * @param taskTree
+     */
+    public void dumpFullTaskTree(ITaskTree taskTree) throws FileNotFoundException {
+        PrintWriter out = null;
+        try {
+            out = new PrintWriter(new FileOutputStream("taskTree.txt"));
+            dumpFullNode(taskTree.getRoot(), out, "", 0);
+        }
+        finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+
+    }
+
+    /**
+     *
+     */
+    private void dumpFullNode(ITaskTreeNode node, PrintWriter out, String indent, int index) {
+        if (node instanceof ISequence) {
+            if (index > 0) {
+                out.println();
+            }
+            out.print(indent);
+            out.println("Sequence {");
+        }
+        else if (node instanceof IIteration) {
+            if (index > 0) {
+                out.println();
+            }
+            out.print(indent);
+            out.println("Iteration {");
+        }
+        else if (node instanceof ISelection) {
+            if (index > 0) {
+                out.println();
+            }
+            out.print(indent);
+            out.println("Selection {");
+        }
+        else if (node instanceof IEventTask) {
+            out.print(indent);
+            out.print(((IEventTask) node).getEventType().getName());
+            out.print(" ");
+            out.print(((IEventTask) node).getEventTarget());
+//            if (((IEventTask) node).getEventTarget() instanceof IGUIElement) {
+//              out.print(" ");
+//              out.print(((IGUIElement) ((IEventTask) node).getEventTarget()).getSpecification());
+//            }
+        }
+        else {
+            fail("unknown type of node in task tree " + node);
+        }
+
+        int i = 0;
+        for (ITaskTreeNode child : node.getChildren()) {
+            dumpFullNode(child, out, indent + "  ", i++);
+        }
+
+        if (!(node instanceof IEventTask)) {
+            out.print(indent);
+            out.print("}");
+        }
+
+        out.println();
+    }
+
+    /**
+     * 
+     */
+    private TaskSpec parseTask(Matcher taskMatcher) {
+        if ("}".equals(taskMatcher.group(1))) {
+            throw new IllegalArgumentException("invalid task specification");
+        }
+        
+        String taskDetails = taskMatcher.group(1);
+        
+        Matcher matcher = taskDetailsPattern.matcher(taskDetails);
+        
+        if (!matcher.find()) {
+            throw new IllegalArgumentException("could not parse task details");
+        }
+
+        TaskSpec task = new TaskSpec();
+        task.type = matcher.group(1);
+        
+        task.name = matcher.group(2);
+        if ((matcher.group(4) != null) && (!"".equals(matcher.group(4).trim()))) {
+            task.name += " " + matcher.group(4).trim();
+        }
+        
+        if ((matcher.group(5) != null) && (!"".equals(matcher.group(5).trim()))) {
+            task.additionalInfo = matcher.group(5).trim();
+        }
+
+        if ("".equals(task.name)) {
+            task.name = null;
+        }
+
+        List<TaskSpec> children = new ArrayList<TaskSpec>();
+        while (taskMatcher.find() && !"}".equals(taskMatcher.group(0))) {
+            children.add(parseTask(taskMatcher));
+        }
+
+        if (children.size() > 0) {
+            task.children = children.toArray(new TaskSpec[children.size()]);
+        }
+
+        return task;
+    }
+
+    /**
+     * @param task
+     * @param taskMapCopy
+     */
+    private void assertTaskAndChildrenInMapAndRemove(TaskSpec                    task,
+                                                     Map<ITaskTreeNode, Integer> taskMap)
+    {
+        for (Map.Entry<ITaskTreeNode, Integer> entry : taskMap.entrySet()) {
+            if (taskSpecEqualsTask(task, entry.getKey())) {
+                if (task.children != null) {
+                    for (TaskSpec child : task.children) {
+                        assertTaskAndChildrenInMapAndRemove(child, taskMap);
+                    }
+                }
+
+                int count = taskMap.get(entry.getKey());
+                if (count == 1) {
+                    taskMap.remove(entry.getKey());
+                }
+                else {
+                    taskMap.put(entry.getKey(), count - 1);
+                }
+                return;
+            }
+        }
+
+        fail("expected task " + task.type + " " + task.name +
+             " not included in task map");
+    }
+
+    /**
+     *
+     */
+    private boolean taskSpecEqualsTask(TaskSpec taskSpec, ITaskTreeNode task) {
+        if (doTrace) {
+            System.err.println("comparing " + taskSpec.name + " with");
+            dumpTask(task, 0, "");
+        }
+
+        if (("Event".equals(taskSpec.type) && (!(task instanceof IEventTask))) ||
+            ("TextInputEvent".equals(taskSpec.type) &&
+             ((!(task instanceof IEventTask)) ||
+              (!(((IEventTask) task).getEventType() instanceof TextInput)))) ||
+            ("Sequence".equals(taskSpec.type) && (!(task instanceof ISequence))) ||
+            ("Selection".equals(taskSpec.type) && (!(task instanceof ISelection))) ||
+            ("Iteration".equals(taskSpec.type) && (!(task instanceof IIteration))))
+        {
+            if (doTrace) {
+                System.err.println("task types do not match: " + taskSpec.type + " != " +
+                    task.getClass().getSimpleName() + "\n");
+            }
+            return false;
+        }
+        else if (!"Event".equals(taskSpec.type) &&
+                 !"TextInputEvent".equals(taskSpec.type) &&
+                 !"Sequence".equals(taskSpec.type) &&
+                 !"Selection".equals(taskSpec.type) &&
+                 !"Iteration".equals(taskSpec.type))
+        {
+            fail("unknown task type " + taskSpec.type + " --> please extend test case");
+        }
+
+        if ("TextInputEvent".equals(taskSpec.type)) {
+            TextInput eventType = (TextInput) ((IEventTask) task).getEventType();
+            if ((taskSpec.additionalInfo != null) &&
+                !"".equals(taskSpec.additionalInfo) &&
+                !(taskSpec.additionalInfo.equals(eventType.getEnteredText())))
+            {
+                if (doTrace) {
+                    System.err.println("expected text \"" + taskSpec.additionalInfo +
+                                       "\" is not equal to the text " + "provided by the task \"" +
+                                       eventType.getEnteredText() + "\"\n");
+                }
+                return false;
+            }
+        }
+        else if ((task instanceof IEventTask) && (((IEventTask) task).getEventType() != null) &&
+                 (!taskSpec.name.equals(((IEventTask) task).getEventType().getName())))
+        {
+            // simple event names do not match. But what about the event name in
+            // combination with the additional info
+            String complexName =
+                taskSpec.name +
+                    (!"".equals(taskSpec.additionalInfo) ? " " + taskSpec.additionalInfo : "");
+
+            if (!complexName.equals(((IEventTask) task).getEventType().getName())) {
+                if (doTrace) {
+                    System.err.println("event names do not match: " + taskSpec.name + " != " +
+                                       ((IEventTask) task).getEventType().getName() + "\n");
+                }
+                return false;
+            }
+        }
+
+        if (((taskSpec.children == null) && (task.getChildren().size() > 0)) ||
+            ((taskSpec.children != null) && (taskSpec.children.length != task.getChildren().size())))
+        {
+            if (doTrace) {
+                System.err.println
+                    ("numbers of children do not match: " +
+                     (taskSpec.children == null ? "0" : taskSpec.children.length) + " != " +
+                     (task.getChildren() == null ? "0" : task.getChildren().size()) + "\n");
+            }
+            return false;
+        }
+
+        Iterator<ITaskTreeNode> children = task.getChildren().iterator();
+        if (taskSpec.children != null) {
+            for (TaskSpec child : taskSpec.children) {
+                if (!taskSpecEqualsTask(child, children.next())) {
+                    if (doTrace) {
+                        System.err.println("one of the children does not match\n");
+                    }
+                    return false;
+                }
+            }
+        }
+
+        if (!children.hasNext()) {
+            if (doTrace) {
+                System.err.println("nodes match\n");
+            }
+            return true;
+        }
+        else {
+            if (doTrace) {
+                System.err.println("number of children does not match\n");
+            }
+            return false;
+        }
+    }
+
+    /**
+   *
+   */
+    private void dumpTaskMap(Map<ITaskTreeNode, Integer> taskMap) {
+        System.err.println();
+        for (Map.Entry<ITaskTreeNode, Integer> entry : taskMap.entrySet()) {
+            dumpTask(entry.getKey(), entry.getValue(), "");
+            System.err.println();
+        }
+    }
+
+    /**
+     *
+     */
+    private void dumpTask(ITaskTreeNode task, int count, String indent) {
+        System.err.print(indent);
+        System.err.print(task);
+        System.err.print(" ");
+        System.err.print(task.getDescription());
+        System.err.print(" ");
+
+        if (count > 0) {
+            System.err.print("(");
+            System.err.print(count);
+            System.err.print(" occurrences)");
+        }
+
+        System.err.println();
+
+        if ((task.getChildren() != null) && (task.getChildren().size() > 0)) {
+            for (ITaskTreeNode child : task.getChildren()) {
+                dumpTask(child, 0, indent + "  ");
+            }
+        }
+    }
+    
+    /**
+     * TODO comment
+     * 
+     * @version $Revision: $ $Date: $
+     * @author 2011, last modified by $Author: $
+     */
+    private class TaskSpec {
+        public String type;
+        public String name;
+        public String additionalInfo;
+        public TaskSpec[] children;
+    }
+
+}
Index: trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/CommandRunner.java
===================================================================
--- trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/CommandRunner.java	(revision 922)
+++ trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/CommandRunner.java	(revision 922)
@@ -0,0 +1,53 @@
+// Module    : $RCSfile: CommandRunner.java,v $
+// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 29.08.2012 $
+// Project   : quest-test-utils
+// Creation  : 2012 by pharms
+// Copyright : Patrick Harms, 2012
+package de.ugoe.cs.autoquest.test;
+
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.util.console.Command;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @version $Revision: $ $Date: 29.08.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class CommandRunner {
+
+    /**
+     * <p>
+     * prevent instantiation
+     * </p>
+     */
+    private CommandRunner() {
+        // just to prevent instantiation
+    }
+
+    public static void runCommand(Class<? extends Command> commandType, Object... parameters) {
+        Command command;
+        try {
+            command = commandType.newInstance();
+        }
+        catch (Exception e) {
+            fail("could not instantiate command");
+            return; // just added to prevent compiler warning. But the fail will already throw an
+                    // exception
+        }
+        
+        List<Object> params = new ArrayList<Object>();
+        
+        for (Object parameter : parameters) {
+            params.add(parameter);
+        }
+        
+        command.run(params);
+    }
+}
Index: trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyGUIElement.java
===================================================================
--- trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyGUIElement.java	(revision 922)
+++ trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyGUIElement.java	(revision 922)
@@ -0,0 +1,81 @@
+
+package de.ugoe.cs.autoquest.test;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.AbstractDefaultGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 02.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DummyGUIElement extends AbstractDefaultGUIElement implements IGUIElement {
+
+    /**  */
+    private static final long serialVersionUID = 1L;
+
+    /** */
+    private String name;
+
+    /**
+     *
+     */
+    public DummyGUIElement(String name) {
+        this(name, null);
+    }
+
+    /**
+     *
+     */
+    public DummyGUIElement(String name, IGUIElement parent) {
+        super(null, parent);
+        this.name = name;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getPlatform()
+     */
+    @Override
+    public String getPlatform() {
+        return "Dummy";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.autoquest.eventcore.IEventTarget#getStringIdentifier()
+     */
+    @Override
+    public String getStringIdentifier() {
+        return name;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.GUIElement#equals(GUIElement)
+     */
+    public boolean equals(IGUIElement other) {
+        return this == other;
+    }
+
+    @Override
+    public void updateSpecification(IGUIElementSpec specToIntegrateElementFor) {
+        // dummy
+    }
+
+}
Index: trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyInteraction.java
===================================================================
--- trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyInteraction.java	(revision 922)
+++ trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyInteraction.java	(revision 922)
@@ -0,0 +1,83 @@
+package de.ugoe.cs.autoquest.test;
+
+import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 02.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DummyInteraction implements IInteraction {
+    
+    /**  */
+    private static final long serialVersionUID = 1L;
+
+    /** */
+    private int interactionNumber;
+
+    /** */
+    private String interactionType;
+
+    /**
+     * @param interactionNumber
+     * @param interactionType
+     */
+    public DummyInteraction(String interactionType, int interactionNumber) {
+        super();
+        this.interactionNumber = interactionNumber;
+        this.interactionType = interactionType;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#getName()
+     */
+    public String getName() {
+        return interactionType;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return interactionType;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#startsLogicalSequence()
+     */
+    public boolean startsLogicalSequence() {
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.harms.attef.userinteraction.Interaction#finishesLogicalSequence()
+     */
+    public boolean finishesLogicalSequence() {
+        return false;
+    }
+
+    /**
+     * @return the interactionType
+     */
+    public String getInteractionType() {
+        return interactionType;
+    }
+
+    /**
+     * @return the interactionNumber
+     */
+    public int getInteractionNumber() {
+        return interactionNumber;
+    }
+
+}
Index: trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyTextField.java
===================================================================
--- trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyTextField.java	(revision 922)
+++ trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/test/DummyTextField.java	(revision 922)
@@ -0,0 +1,23 @@
+package de.ugoe.cs.autoquest.test;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 02.04.2012$
+ * @author 2012, last modified by $Author: patrick$
+ */
+public class DummyTextField extends DummyGUIElement implements ITextField {
+
+    /**  */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    public DummyTextField(String name) {
+        super("text field \"" + name + "\"");
+    }
+
+}
Index: trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/usageprofiles/MockTrieBasedModel.java
===================================================================
--- trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/usageprofiles/MockTrieBasedModel.java	(revision 922)
+++ trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/usageprofiles/MockTrieBasedModel.java	(revision 922)
@@ -0,0 +1,31 @@
+package de.ugoe.cs.autoquest.usageprofiles;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+
+public class MockTrieBasedModel extends TrieBasedModel {
+	private static final long serialVersionUID = 1L;
+
+	public MockTrieBasedModel(int markovOrder, Random r) {
+		super(markovOrder, r);
+	}
+
+	@Override
+	public double getProbability(List<Event> context,
+			Event symbol) {
+		List<Event> list = new ArrayList<Event>();
+		if( context.isEmpty() ) {
+			return 2;
+		}
+		list.add(context.get(context.size()-1));
+		if( trie.find(list).getFollowingSymbols().contains(symbol) ) {
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+}
Index: trunk/autoquest-test/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-test/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-test/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-ui-core/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest-ui-core/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest-ui-core/.settings/org.eclipse.m2e.core.prefs	(revision 922)
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDdeleteObject.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDdeleteObject.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDdeleteObject.java	(revision 922)
@@ -0,0 +1,48 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to delete object from {@link GlobalDataContainer}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDdeleteObject implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "deleteObject <objectname>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String objectName;
+		try {
+			objectName = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+		boolean deleted = GlobalDataContainer.getInstance().removeData(
+				objectName);
+		if (!deleted) {
+			CommandHelpers.objectNotFoundMessage(objectName);
+		}
+	}
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDload.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDload.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDload.java	(revision 922)
@@ -0,0 +1,60 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.List;
+
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command that loads a previously serialized {@link GlobalDataContainer}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDload implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		try {
+			filename = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		FileInputStream fis = null;
+		ObjectInputStream in = null;
+		try {
+			fis = new FileInputStream(filename);
+			in = new ObjectInputStream(fis);
+			in.readObject();
+			in.close();
+		} catch (IOException ex) {
+			Console.logException(ex);
+		} catch (ClassNotFoundException ex) {
+			Console.logException(ex);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "load <filename>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDloadObject.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDloadObject.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDloadObject.java	(revision 922)
@@ -0,0 +1,68 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to load a previously serialized object and store it in the
+ * {@link GlobalDataContainer}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDloadObject implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		String objectName;
+		try {
+			filename = (String) parameters.get(0);
+			objectName = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object data = null;
+		FileInputStream fis = null;
+		ObjectInputStream in = null;
+		try {
+			fis = new FileInputStream(filename);
+			in = new ObjectInputStream(fis);
+			data = in.readObject();
+			in.close();
+		} catch (IOException ex) {
+			Console.logException(ex);
+		} catch (ClassNotFoundException ex) {
+			Console.logException(ex);
+		}
+		if (GlobalDataContainer.getInstance().addData(objectName, data)) {
+			CommandHelpers.dataOverwritten(objectName);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "loadObject <filename> <objectName>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDsave.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDsave.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDsave.java	(revision 922)
@@ -0,0 +1,58 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.List;
+
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to save the {@link GlobalDataContainer} through serialization.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDsave implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		try {
+			filename = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		FileOutputStream fos = null;
+		ObjectOutputStream out = null;
+		try {
+			fos = new FileOutputStream(filename);
+			out = new ObjectOutputStream(fos);
+			out.writeObject(GlobalDataContainer.getInstance());
+			out.close();
+		} catch (IOException ex) {
+			Console.logException(ex);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "save <filename>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDsaveObject.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDsaveObject.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDsaveObject.java	(revision 922)
@@ -0,0 +1,69 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command that saves an object contained in the {@link GlobalDataContainer}
+ * through serialization.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDsaveObject implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		String objectName;
+		try {
+			filename = (String) parameters.get(0);
+			objectName = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance().getData(
+				objectName);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(objectName);
+			return;
+		}
+
+		FileOutputStream fos = null;
+		ObjectOutputStream out = null;
+		try {
+			fos = new FileOutputStream(filename);
+			out = new ObjectOutputStream(fos);
+			out.writeObject(dataObject);
+			out.close();
+		} catch (IOException ex) {
+			Console.logException(ex);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "saveObject <filename> <objectName>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDshowTimer.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDshowTimer.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDshowTimer.java	(revision 922)
@@ -0,0 +1,60 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to show the time elapsed since a timer has been started.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDshowTimer implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String timerName;
+		try {
+			timerName = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance().getData(timerName);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(timerName);
+			return;
+		}
+		if (!(dataObject instanceof Long)) {
+			CommandHelpers.objectNotType(timerName, "Long");
+			return;
+		}
+
+		long startTime = (Long) dataObject;
+		long currentTime = System.currentTimeMillis();
+		Console.traceln(Level.INFO, "" + (currentTime - startTime) + " milliseconds");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "showTimer <timerName>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstartFileListener.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstartFileListener.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstartFileListener.java	(revision 922)
@@ -0,0 +1,51 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.FileOutputListener;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to start a {@link FileOutputListener}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDstartFileListener implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		try {
+			filename = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		FileOutputListener listener = new FileOutputListener(filename);
+		listener.start();
+		if (GlobalDataContainer.getInstance().addData(filename, listener)) {
+			CommandHelpers.dataOverwritten(filename);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "startFileListener <filename>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstartTimer.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstartTimer.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstartTimer.java	(revision 922)
@@ -0,0 +1,48 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to start a time.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDstartTimer implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String timerName;
+		try {
+			timerName = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+		Long time = System.currentTimeMillis();
+		if (GlobalDataContainer.getInstance().addData(timerName, time)) {
+			CommandHelpers.dataOverwritten(timerName);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "startTimer <timerName>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstopFileListener.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstopFileListener.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/misc/CMDstopFileListener.java	(revision 922)
@@ -0,0 +1,49 @@
+package de.ugoe.cs.autoquest.commands.misc;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.FileOutputListener;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to stop a {@link FileOutputListener}.
+ * </p>
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDstopFileListener implements Command {
+
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		try {
+			filename = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance().getData(filename);
+		if( dataObject==null ) {
+			CommandHelpers.objectNotFoundMessage(filename);
+			return;
+		}
+		if( !(dataObject instanceof FileOutputListener) ) {
+			CommandHelpers.objectNotType(filename, "FileOutputListener");
+			return;
+		}
+		
+		FileOutputListener listener = (FileOutputListener) dataObject;
+		listener.stop();
+		GlobalDataContainer.getInstance().removeData(filename);
+	}
+
+	@Override
+	public String help() {
+		return "stopFileListener <filename>";
+
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcondenseGuiModel.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcondenseGuiModel.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcondenseGuiModel.java	(revision 922)
@@ -0,0 +1,59 @@
+
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to condense the GUI model of a sequence, i.e., merge duplicate nodes.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class CMDcondenseGuiModel implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName = "sequences";
+
+        if (parameters.size() > 0) {
+            sequencesName = (String) parameters.get(0);
+        }
+
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName + "_targets");
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName + "_targets");
+            return;
+        }
+        if (!(dataObject instanceof GUIModel)) {
+            CommandHelpers.objectNotType(sequencesName, "GUIModel");
+            return;
+        }
+
+        GUIModel model = (GUIModel) dataObject;
+
+        model.condenseModel();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "condenseGuiModel {<sequencesName>}";
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcondenseMouseClicks.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcondenseMouseClicks.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcondenseMouseClicks.java	(revision 922)
@@ -0,0 +1,86 @@
+
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClickCondenser;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * This command condenses mouse clicks, i.e. it reduces a sequence of mouse button down,
+ * mouse button up and mouse click with the same button on the same event target to a single
+ * mouse click with that button on that target. The mouse button down and mouse button up events
+ * are discarded.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDcondenseMouseClicks implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "condenseMouseClicks <sequences> {<new sequences>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        String newSequencesName;
+        try {
+            sequencesName = (String) parameters.get(0);
+            if (parameters.size() > 1) {
+                newSequencesName = (String) parameters.get(1);
+            }
+            else {
+                newSequencesName = sequencesName;
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide a sequences name");
+        }
+
+        Collection<List<Event>> sequences = null;
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        sequences = (Collection<List<Event>>) dataObject;
+
+        Collection<List<Event>> newSequences = new LinkedList<List<Event>>();
+        
+        for (List<Event> sequence : sequences) {
+            newSequences.add(new MouseClickCondenser().condenseMouseClicks(sequence));
+        }
+
+        if (GlobalDataContainer.getInstance().addData(newSequencesName, newSequences)) {
+            CommandHelpers.dataOverwritten(newSequencesName);
+        }
+        
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcorrectKeyInteractionTargets.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcorrectKeyInteractionTargets.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDcorrectKeyInteractionTargets.java	(revision 922)
@@ -0,0 +1,87 @@
+
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteractionTargetCorrector;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * This command iterates the provided sequences and sets the target of all key interaction events
+ * to the GUI element having the current keyboard focus. The current keyboard focus is determined
+ * either by keyboard focus events or by using the target of the first key interaction in a
+ * sequence. Events changing the keyboard focus are discarded herewith.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDcorrectKeyInteractionTargets implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "correctKeyInteractionTargets <sequences> {<new sequences>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        String newSequencesName;
+        try {
+            sequencesName = (String) parameters.get(0);
+            if (parameters.size() > 1) {
+                newSequencesName = (String) parameters.get(1);
+            }
+            else {
+                newSequencesName = sequencesName;
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide a sequences name");
+        }
+
+        Collection<List<Event>> sequences = null;
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        sequences = (Collection<List<Event>>) dataObject;
+
+        Collection<List<Event>> newSequences = new LinkedList<List<Event>>();
+        
+        for (List<Event> sequence : sequences) {
+            newSequences.add
+                (new KeyInteractionTargetCorrector().correctKeyInteractionTargets(sequence));
+        }
+
+        if (GlobalDataContainer.getInstance().addData(newSequencesName, newSequences)) {
+            CommandHelpers.dataOverwritten(newSequencesName);
+        }
+        
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDdetectTextInputEvents.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDdetectTextInputEvents.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDdetectTextInputEvents.java	(revision 922)
@@ -0,0 +1,104 @@
+
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyPressed;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyReleased;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyTyped;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInputDetector;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput.TextEquality;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command that converts {@link KeyPressed}, {@link KeyReleased}, and {@link KeyTyped} sequences
+ * into {@link TextInput} events if possible.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDdetectTextInputEvents implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "detectTextInputEvents <sequences> {<new sequences>} {<textEqualityType>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        String newSequencesName;
+        String textEqualityTypeString = "LEXICAL";
+        try {
+            sequencesName = (String) parameters.get(0);
+            if (parameters.size() > 1) {
+                newSequencesName = (String) parameters.get(1);
+            }
+            else {
+                newSequencesName = sequencesName;
+            }
+            if (parameters.size() > 2) {
+                textEqualityTypeString = (String) parameters.get(2);
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide a sequences name");
+        }
+
+        Collection<List<Event>> sequences = null;
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        TextEquality textEqualityType = null;
+        try {
+            textEqualityType = TextEquality.valueOf(textEqualityTypeString);
+        }
+        catch (IllegalArgumentException e) {
+            Console
+                .printerrln("Invalid mode. Only LEXICAL, SYNTACTICAL, and SEMANTICAL are allowed values!");
+            return;
+        }
+
+        sequences = (Collection<List<Event>>) dataObject;
+
+        Collection<List<Event>> newSequences = new LinkedList<List<Event>>();
+
+        for (List<Event> sequence : sequences) {
+            newSequences.add(new TextInputDetector(textEqualityType).detectTextInputs(sequence));
+        }
+
+        if (GlobalDataContainer.getInstance().addData(newSequencesName, newSequences)) {
+            CommandHelpers.dataOverwritten(newSequencesName);
+        }
+
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDgenerateReplayfile.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDgenerateReplayfile.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDgenerateReplayfile.java	(revision 922)
@@ -0,0 +1,198 @@
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.IReplayDecorator;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IReplayable;
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to create a replay file from stored sessions.
+ * </p>
+ * 
+ * TODO: Add appropriate checks if Events are replayable
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDgenerateReplayfile implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "generateReplayfile <filename> <sequences>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public void run(List<Object> parameters) {
+		String filename;
+		String sequencesName;
+		try {
+			filename = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Collection<List<Event>> sequences = null;
+		Object dataObject = GlobalDataContainer.getInstance().getData(
+				sequencesName);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(sequencesName);
+			return;
+		}
+		if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+			CommandHelpers.objectNotType(sequencesName,
+					"Collection<List<Event<?>>>");
+			return;
+		}
+
+		sequences = (Collection<List<Event>>) dataObject;
+		createLogfileMultipleSessions(sequences, filename);
+	}
+	
+	    /**
+	     * <p>
+	     * {@link IReplayDecorator} to be used. If this field is {@code null}, no decorator is used.
+	     * Default: {@code null}
+	     * </p>
+	     */
+	    private IReplayDecorator decorator = null;
+
+	    /**
+	     * <p>
+	     * Id of the current session. The starting id is 1.
+	     * </p>
+	     */
+	    int sessionId = 1;
+
+	    /**
+	     * <p>
+	     * Creates a replay file that contains multiple event sequences.
+	     * </p>
+	     * 
+	     * @param sequences
+	     *            collection of event sequences from which the sessions are generated
+	     * @param filename
+	     *            name and path of the replay file
+	     */
+	    private void createLogfileMultipleSessions(Collection<List<Event>> sequences, String filename) {
+	        OutputStreamWriter writer = openReplayFile(filename);
+	        if (writer != null) {
+	            try {
+	                try {
+	                    decorator =
+	                        sequences.iterator().next().get(0).getReplayables().get(0).getDecorator();
+	                }
+	                catch (Exception e) {
+	                    // in the above line, many things can go wrong: emtpy sequences, null
+	                    // references, etc. However, all failures just indicate that no replay decorator
+	                    // should be used, hence, we ignore the exception
+	                }
+	                if (decorator != null) {
+	                    writer.write(decorator.getHeader());
+	                }
+	                for (List<Event> actions : sequences) {
+	                    writeSession(actions, writer);
+	                }
+	                if (decorator != null) {
+	                    writer.write(decorator.getFooter());
+	                }
+	                decorator = null;
+	                writer.close();
+	            }
+	            catch (IOException e) {
+	                Console.printerrln("Unable to write replay file " + filename);
+	            }
+	        }
+	    }
+
+	    /**
+	     * <p>
+	     * Helper function that opens the replay file for writing.
+	     * </p>
+	     * 
+	     * @param filename
+	     *            name and path of the replay file
+	     * @return {@link OutputStreamWriter} that writes to the replay file
+	     */
+	    private OutputStreamWriter openReplayFile(String filename) {
+	        File file = new File(filename);
+	        boolean fileCreated;
+	        try {
+	            fileCreated = file.createNewFile();
+	            if (!fileCreated) {
+	                Console.traceln(Level.INFO, "Created logfile " + filename);
+	            }
+	            else {
+	                Console.traceln(Level.INFO, "Overwrote existing logfile " + filename);
+	            }
+	        }
+	        catch (IOException e) {
+	            Console.printerrln("Unable to create file " + filename);
+	            Console.logException(e);
+	        }
+	        OutputStreamWriter writer = null;
+	        try {
+	            writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-16");
+	        }
+	        catch (IOException e) {
+	            Console.printerrln("Unable to open file for writing (read-only file):" + filename);
+	            Console.logException(e);
+	        }
+	        return writer;
+	    }
+
+	    /**
+	     * <p>
+	     * Helper function that adds an event sequence to the replay.
+	     * </p>
+	     * 
+	     * @param actions
+	     *            event sequences to be added
+	     * @param writer
+	     *            {@link OutputStreamWriter} to which the replay is added
+	     * @throws IOException
+	     *             thrown if there is a problem writing to writer
+	     */
+	    private void writeSession(List<Event> actions, OutputStreamWriter writer) throws IOException {
+	        if (decorator != null) {
+	            writer.write(decorator.getSessionHeader(sessionId));
+	        }
+	        for (Event currentAction : actions) {
+
+	            List<? extends IReplayable> replayables = currentAction.getReplayables();
+	            for (IReplayable replayble : replayables) {
+	                writer.write(replayble.getReplay() + StringTools.ENDLINE);
+	                writer.flush();
+	            }
+	        }
+	        if (decorator != null) {
+	            writer.write(decorator.getSessionFooter(sessionId));
+	        }
+	        sessionId++;
+	    }
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDremoveStartEndSymbols.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDremoveStartEndSymbols.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDremoveStartEndSymbols.java	(revision 922)
@@ -0,0 +1,75 @@
+
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to remove the {@link Event#STARTEVENT} and {@link Event#ENDEVENT} from a sequence.
+ * </p>
+ * 
+ * @version 1.0
+ * @author Steffen Herbold
+ */
+public class CMDremoveStartEndSymbols implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        try {
+            sequencesName = (String) parameters.get(0);
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+
+        Collection<List<Event>> sequences = null;
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        sequences = (Collection<List<Event>>) dataObject;
+
+        for (List<Event> sequence : sequences) {
+            Iterator<Event> iter = sequence.iterator();
+            while (iter.hasNext()) {
+                Event event = iter.next();
+                if (Event.ENDEVENT.equals(event) || Event.STARTEVENT.equals(event)) {
+                    iter.remove();
+                }
+            }
+        }
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "removeStartEndSymbols <sequences>";
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDsequenceStatistics.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDsequenceStatistics.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDsequenceStatistics.java	(revision 922)
@@ -0,0 +1,82 @@
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to print basic statistical information about stored sequences, e.g.,
+ * how many there are of which length.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDsequenceStatistics implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public void run(List<Object> parameters) {
+		String sequencesName;
+		try {
+			sequencesName = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Collection<List<Event>> sequences = null;
+		Object dataObject = GlobalDataContainer.getInstance().getData(
+				sequencesName);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(sequencesName);
+			return;
+		}
+		if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+			CommandHelpers.objectNotType(sequencesName,
+					"Collection<List<Event<?>>>");
+			return;
+		}
+
+		sequences = (Collection<List<Event>>) dataObject;
+		Console.println("Number of Sequences: " + sequences.size());
+		SortedMap<Integer, Integer> lengthMap = new TreeMap<Integer, Integer>();
+		for (List<Event> sequence : sequences) {
+			Integer currentSize = sequence.size();
+			if (lengthMap.containsKey(currentSize)) {
+				lengthMap.put(currentSize, lengthMap.get(currentSize) + 1);
+			} else {
+				lengthMap.put(currentSize, 1);
+			}
+		}
+		for (Entry<Integer, Integer> entry : lengthMap.entrySet()) {
+			Console.println("Of length " + entry.getKey() + ": "
+					+ entry.getValue());
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "sequenceStatistics <sequencesName>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDsortKeyInteractions.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDsortKeyInteractions.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDsortKeyInteractions.java	(revision 922)
@@ -0,0 +1,121 @@
+
+package de.ugoe.cs.autoquest.commands.sequences;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteractionCorrector;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteractionCorrector.CleanupMode;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to sort the key interactions in a sequence of events. An example, the sequence to write
+ * the upper case D
+ * <ul>
+ * <li>press shift key</li>
+ * <li>press D key</li>
+ * <li>release shift key</li>
+ * <li>release D key</li>
+ * </ul>
+ * 
+ * is transformed to the sequence
+ * 
+ * <ul>
+ * <li>press shift key</li>
+ * <li>press D key</li>
+ * <li>release D key</li>
+ * <li>release shift key</li>
+ * </ul>
+ * 
+ * in which the first pressed key (shift in this case) is always released last. The same is done for
+ * the alt and the ctrl keys.
+ * 
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDsortKeyInteractions implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "sortKeyInteractions <sequences> {<new sequences>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        String newSequencesName;
+        String modeString = "ADDITION";
+        try {
+            sequencesName = (String) parameters.get(0);
+            if (parameters.size() > 1) {
+                newSequencesName = (String) parameters.get(1);
+            }
+            else {
+                newSequencesName = sequencesName;
+            }
+            if (parameters.size() > 2) {
+                modeString = (String) parameters.get(2);
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide a sequences name");
+        }
+
+        Collection<List<Event>> sequences = null;
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        CleanupMode mode = null;
+        try {
+            mode = CleanupMode.valueOf(modeString);
+        }
+        catch (IllegalArgumentException e) {
+            Console.printerrln("Invalid mode. Only REMOVAL and ADDITION are allowed values!");
+            return;
+        }
+
+        sequences = (Collection<List<Event>>) dataObject;
+
+        Collection<List<Event>> newSequences = new LinkedList<List<Event>>();
+
+        int i = 1;
+        for (List<Event> sequence : sequences) {
+            Console.traceln(Level.INFO, "Processing sequence " + i++);
+            newSequences.add(new KeyInteractionCorrector(mode).sortKeyInteractions(sequence));
+        }
+
+        if (GlobalDataContainer.getInstance().addData(newSequencesName, newSequences)) {
+            CommandHelpers.dataOverwritten(newSequencesName);
+        }
+
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDevaluateUsability.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDevaluateUsability.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDevaluateUsability.java	(revision 922)
@@ -0,0 +1,78 @@
+
+package de.ugoe.cs.autoquest.commands.usability;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.autoquest.usability.UsabilityEvaluationManager;
+import de.ugoe.cs.autoquest.usability.UsabilityEvaluationResult;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * This command performs a usability evaluation based on a task tree. It uses the
+ * {@link UsabilityEvaluationManager} for this purpose. Please consult the documentation of the
+ * usability evaluation manager for more details.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDevaluateUsability implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "evaluateUsability <tasktree> {<evaluationResult>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String tasktreeName;
+        String evaluationResult;
+        try {
+            tasktreeName = (String) parameters.get(0);
+            if (parameters.size() > 1) {
+                evaluationResult = (String) parameters.get(1);
+            }
+            else {
+                evaluationResult = "usabilityEvaluationResult";
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide a task tree name");
+        }
+
+        Object dataObject = GlobalDataContainer.getInstance().getData(tasktreeName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(tasktreeName);
+            return;
+        }
+        if (!(dataObject instanceof ITaskTree)) {
+            CommandHelpers.objectNotType(tasktreeName, "ITaskTree");
+            return;
+        }
+
+        ITaskTree taskTree = (ITaskTree) dataObject;
+        
+        UsabilityEvaluationResult result =
+            new UsabilityEvaluationManager().evaluateUsability(taskTree);
+        
+        if (GlobalDataContainer.getInstance().addData(evaluationResult, result)) {
+            CommandHelpers.dataOverwritten(evaluationResult);
+        }
+        
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDgenerateTaskTree.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDgenerateTaskTree.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDgenerateTaskTree.java	(revision 922)
@@ -0,0 +1,81 @@
+
+package de.ugoe.cs.autoquest.commands.usability;
+
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.tasktrees.manager.TaskTreeManager;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTree;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * This command generates a task tree based on the provided sequences. It uses the
+ * {@link TaskTreeManager} for this purpose. Please consult the documentation of the task tree
+ * manager for more details.
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDgenerateTaskTree implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "generateTaskTree <sequences> {<tasktree>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        String tasktreeName;
+        try {
+            sequencesName = (String) parameters.get(0);
+            if (parameters.size() > 1) {
+                tasktreeName = (String) parameters.get(1);
+            }
+            else {
+                tasktreeName = "tasktree";
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide a sequences name");
+        }
+
+        Collection<List<Event>> sequences = null;
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        sequences = (Collection<List<Event>>) dataObject;
+        
+        ITaskTree taskTree = new TaskTreeManager().createTaskTree(sequences);
+        
+        if (GlobalDataContainer.getInstance().addData(tasktreeName, taskTree)) {
+            CommandHelpers.dataOverwritten(sequencesName);
+        }
+        
+    }
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/AbstractTrainCommand.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/AbstractTrainCommand.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/AbstractTrainCommand.java	(revision 922)
@@ -0,0 +1,92 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Abstract class for commands to train {@link TrieBasedModel}s.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public abstract class AbstractTrainCommand implements Command {
+
+	/**
+	 * <p>
+	 * Handling of additional parameters.
+	 * </p>
+	 * 
+	 * @param parameters
+	 *            same as the parameters passed to {@link #run(List)}.
+	 * @throws Exception
+	 *             thrown, if there is an error parsing the parameters
+	 */
+	abstract void handleAdditionalParameters(List<Object> parameters)
+			throws Exception;
+
+	/**
+	 * <p>
+	 * Returns a concrete instance of {@link TrieBasedModel} to be trained. This
+	 * is a factory method.
+	 * </p>
+	 * 
+	 * @return instance of {@link TrieBasedModel}
+	 */
+	abstract TrieBasedModel createModel();
+
+	/**
+	 * <p>
+	 * The command is implemented as a template method. The general structure of
+	 * the command is always the same, only the parameters of the command and
+	 * the creation of the {@link TrieBasedModel} instance. The former is
+	 * handled by {@link #handleAdditionalParameters(List)}, the latter by
+	 * {@link #createModel()}.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String sequencesName;
+
+		try {
+			modelname = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			handleAdditionalParameters(parameters);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance().getData(
+				sequencesName);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(sequencesName);
+			return;
+		}
+		if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+			CommandHelpers.objectNotType(sequencesName,
+					"Collection<List<Event<?>>>");
+			return;
+		}
+		Collection<List<Event>> sequences = (Collection<List<Event>>) dataObject;
+
+		TrieBasedModel model = createModel();
+		model.train(sequences);
+		if (GlobalDataContainer.getInstance().addData(modelname, model)) {
+			CommandHelpers.dataOverwritten(modelname);
+		}
+
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDcalcCoverage.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDcalcCoverage.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDcalcCoverage.java	(revision 922)
@@ -0,0 +1,140 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.coverage.CoverageCalculatorObserved;
+import de.ugoe.cs.autoquest.coverage.CoverageCalculatorProcess;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to calculate the coverage of a test suite.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDcalcCoverage implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String observedName;
+		String[] sequenceNames;
+		int minLength;
+		int maxLength;
+		try {
+			modelname = (String) parameters.get(0);
+			observedName = (String) parameters.get(1);
+			sequenceNames = (String[]) parameters.get(2);
+			minLength = Integer.parseInt((String) parameters.get(3));
+			maxLength = Integer.parseInt((String) parameters.get(4));
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		IStochasticProcess process = null;
+		Collection<List<Event>> observedSequences = null;
+		Collection<List<Event>> sequences = null;
+		Object dataObjectProcess = GlobalDataContainer.getInstance().getData(
+				modelname);
+		Object dataObjectObserved = GlobalDataContainer.getInstance().getData(
+				observedName);
+		if (dataObjectProcess == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObjectProcess instanceof IStochasticProcess)) {
+			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
+			return;
+		}
+		if (dataObjectObserved == null) {
+			CommandHelpers.objectNotFoundMessage(observedName);
+			return;
+		}
+		if (!SequenceInstanceOf.isCollectionOfSequences(dataObjectObserved)) {
+			CommandHelpers.objectNotType(observedName,
+					"Collection<List<Event<?>>>");
+			return;
+		}
+		process = (IStochasticProcess) dataObjectProcess;
+		observedSequences = (Collection<List<Event>>) dataObjectObserved;
+
+		Console.print("seqName");
+		for (int length = minLength; length <= maxLength; length++) {
+			Console.print(";numObs_" + length);
+			Console.print(";numCov_" + length);
+			Console.print(";numNew_" + length);
+			Console.print(";numPos_" + length);
+			Console.print(";all_" + length);
+			Console.print(";pos_" + length);
+			Console.print(";poswei_" + length);
+			Console.print(";obs_" + length);
+			Console.print(";obswei_" + length);
+			Console.print(";new_" + length);
+			Console.print(";newpos_" + length);
+			Console.print(";newposwei_" + length);
+		}
+		Console.println("");
+		for (String sequenceName : sequenceNames) {
+			Object dataObjectSequences = GlobalDataContainer.getInstance()
+					.getData(sequenceName);
+			if (dataObjectSequences == null) {
+				CommandHelpers.objectNotFoundMessage(sequenceName);
+				return;
+			} else if (!SequenceInstanceOf
+					.isCollectionOfSequences(dataObjectSequences)) {
+				CommandHelpers.objectNotType(sequenceName,
+						"Collection<List<Event<?>>>");
+				return;
+			}
+			sequences = (Collection<List<Event>>) dataObjectSequences;
+			Console.print(sequenceName);
+			for (int length = minLength; length <= maxLength; length++) {
+				CoverageCalculatorProcess covCalcProc = new CoverageCalculatorProcess(
+						process, sequences, length);
+				CoverageCalculatorObserved covCalcObs = new CoverageCalculatorObserved(
+						observedSequences, sequences, length);
+				Console.print(";" + covCalcObs.getNumObserved());
+				Console.print(";" + covCalcObs.getNumCovered());
+				Console.print(";" + covCalcObs.getNumNew());
+				Console.print(";" + covCalcProc.getNumPossible());
+				Console.print(";" + covCalcProc.getCoverageAllNoWeight());
+				Console.print(";" + covCalcProc.getCoveragePossibleNoWeight());
+				Console.print(";" + covCalcProc.getCoveragePossibleWeight());
+				Console.print(";" + covCalcObs.getCoverageObserved());
+				Console.print(";"
+						+ covCalcObs.getCoverageObservedWeigth(process));
+				Console.print(";" + covCalcObs.getNewPercentage());
+				Console.print(";" + covCalcObs.getCoveragePossibleNew(process));
+				Console.print(";"
+						+ covCalcObs.getCoveragePossibleNewWeight(process));
+			}
+			Console.println("");
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "calcCoverage <modelname> <observedSequences> [<sequenceNames>] <minCovLength> <maxCovLength>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDcalcEntropy.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDcalcEntropy.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDcalcEntropy.java	(revision 922)
@@ -0,0 +1,63 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to calculate the entropy of first-order Markov models.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDcalcEntropy implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "calcEntropy <modelname>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname = "";
+		try {
+			modelname = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		FirstOrderMarkovModel model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof FirstOrderMarkovModel)) {
+			CommandHelpers.objectNotType(modelname, "FirstOrderMarkovModel");
+			return;
+		}
+		model = (FirstOrderMarkovModel) dataObject;
+		double entropy = model.calcEntropy();
+		if (!Double.isNaN(entropy)) {
+			Console.println("entropy: " + entropy);
+		}
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDflattenModel.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDflattenModel.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDflattenModel.java	(revision 922)
@@ -0,0 +1,70 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.HighOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.ModelFlattener;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to flatten high-order models and create first-order markov models
+ * with the same stochastic properties.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDflattenModel implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String modelnameFOM;
+
+		try {
+			modelname = (String) parameters.get(0);
+			modelnameFOM = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof HighOrderMarkovModel)) {
+			CommandHelpers.objectNotType(modelname, "HighOrderMarkovModel");
+			return;
+		}
+
+		HighOrderMarkovModel model = (HighOrderMarkovModel) dataObject;
+		ModelFlattener flattener = new ModelFlattener();
+		FirstOrderMarkovModel modelFOM = flattener
+				.flattenHighOrderMarkovModel(model);
+
+		if (GlobalDataContainer.getInstance().addData(modelnameFOM, modelFOM)) {
+			CommandHelpers.dataOverwritten(modelnameFOM);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "flattenModel <modelname> <modelname_flattened>";
+	}
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateFixedLengthSequences.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateFixedLengthSequences.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateFixedLengthSequences.java	(revision 922)
@@ -0,0 +1,88 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+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.testgeneration.DrawFromAllSequencesGenerator;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to generate all sequences of a given length.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDgenerateFixedLengthSequences implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String sequencesName;
+		int minLength;
+		int maxLength;
+		boolean all = true;
+		int numSequences = -1;
+		boolean validEnd = true;
+		try {
+			modelname = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			minLength = Integer.parseInt((String) parameters.get(2));
+			maxLength = Integer.parseInt((String) parameters.get(3));
+			if (parameters.size() >= 5) {
+				all = Boolean.parseBoolean((String) parameters.get(4));
+			}
+			if (parameters.size() >= 6) {
+				numSequences = Integer.parseInt((String) parameters.get(5));
+			}
+			if (parameters.size() >= 7) {
+				validEnd = Boolean.parseBoolean((String) parameters.get(6));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		IStochasticProcess model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		} else if (!(dataObject instanceof IStochasticProcess)) {
+			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
+			return;
+		}
+		model = (IStochasticProcess) dataObject;
+		DrawFromAllSequencesGenerator generator = new DrawFromAllSequencesGenerator(
+				numSequences, minLength, maxLength, validEnd, all);
+		Collection<List<Event>> sequences = generator
+				.generateTestSuite(model);
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+		Console.println("" + sequences.size() + " sequences generated");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "generateFixedLengthSequences <modelname> <sequencesName> <minlenght> <maxlength> {<all>} {<numSequences>} {<validEnd>}";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateGreedy.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateGreedy.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateGreedy.java	(revision 922)
@@ -0,0 +1,179 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.coverage.SequenceTools;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.ArrayTools;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to generate test suite with a greedy strategy to achieve a desired
+ * coverage.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDgenerateGreedy implements Command {
+
+	/**
+	 * <p>
+	 * Tolerance for double comparisons
+	 * </p>
+	 */
+	final static double eps = 0.000000000001;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String sequencesName;
+		int minLength;
+		int maxLength;
+		int coverageDepth;
+		float desiredCoverage;
+		boolean validEnd = true;
+		try {
+			modelname = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			minLength = Integer.parseInt((String) parameters.get(2));
+			maxLength = Integer.parseInt((String) parameters.get(3));
+			coverageDepth = Integer.parseInt((String) parameters.get(4));
+			desiredCoverage = Float.parseFloat((String) parameters.get(5));
+			if (parameters.size() >= 7) {
+				validEnd = Boolean.parseBoolean((String) parameters.get(6));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		IStochasticProcess model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		} else if (!(dataObject instanceof IStochasticProcess)) {
+			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
+			return;
+		}
+		model = (IStochasticProcess) dataObject;
+
+		// set up everything
+		List<List<Event>> allSequences = new LinkedList<List<Event>>();
+		for (int length = minLength; length <= maxLength; length++) {
+			if (validEnd) {
+				allSequences.addAll(model.generateValidSequences(length + 2));
+			} else {
+				allSequences.addAll(model.generateSequences(length + 1, true));
+			}
+		}
+		Console.traceln(Level.INFO, "" + allSequences.size() + " possible");
+
+		Collection<List<Event>> allSubSeqs = model
+				.generateSequences(coverageDepth);
+		Map<List<Event>, Double> weightMap = SequenceTools
+				.generateWeights(model, allSubSeqs);
+		Set<List<Event>> coveredSubSeqs = new LinkedHashSet<List<Event>>();
+
+		List<Set<List<Event>>> containedSubSeqs = new LinkedList<Set<List<Event>>>();
+		for (List<Event> sequence : allSequences) {
+			List<List<Event>> wrapper = new LinkedList<List<Event>>();
+			wrapper.add(sequence);
+			Set<List<Event>> currentSubSeqs = SequenceTools
+					.containedSubSequences(wrapper, coverageDepth);
+			containedSubSeqs.add(currentSubSeqs);
+		}
+
+		List<List<Event>> testSuite = new LinkedList<List<Event>>();
+		double currentCoverage = 0.0d;
+
+		// Build test suite
+		double prevGain = 1.0d;
+		boolean gainEqual = false;
+		while (currentCoverage < desiredCoverage) {
+			Double[] sequenceGain = new Double[allSequences.size()];
+			int i = 0;
+			for (Set<List<Event>> containedSubSeq : containedSubSeqs) {
+				double gain = 0.0d;
+				Iterator<List<Event>> subSeqIter = containedSubSeq
+						.iterator();
+				while (subSeqIter.hasNext()) {
+					List<Event> subSeq = subSeqIter.next();
+					if (!coveredSubSeqs.contains(subSeq)) {
+						gain += weightMap.get(subSeq);
+					} else {
+						subSeqIter.remove();
+					}
+				}
+				sequenceGain[i] = gain;
+				// optimization using that the gain is monotonically decreasing
+				if (Math.abs(gain - prevGain) <= eps) {
+					gainEqual = true;
+					break;
+				}
+				i++;
+			}
+			int maxIndex;
+			if (gainEqual) {
+				maxIndex = i;
+			} else {
+				maxIndex = ArrayTools.findMax(sequenceGain);
+			}
+			if (maxIndex < 0 || sequenceGain[maxIndex] <= 0.0 + eps) {
+				Console.traceln(Level.WARNING, "No gain anymore! Desired coverage cannot be satisfied!");
+				break;
+			}
+			prevGain = sequenceGain[maxIndex];
+			testSuite.add(allSequences.get(maxIndex));
+			coveredSubSeqs.addAll(containedSubSeqs.get(maxIndex));
+			currentCoverage += sequenceGain[maxIndex];
+			if (gainEqual) {
+				allSequences.remove(maxIndex);
+				containedSubSeqs.remove(maxIndex);
+				gainEqual = false;
+			} else {
+				for (int j = sequenceGain.length - 1; j >= 0; j--) {
+					if (j == maxIndex || sequenceGain[j] <= 0.0 + eps) {
+						allSequences.remove(j);
+						containedSubSeqs.remove(j);
+					}
+				}
+			}
+		}
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName, testSuite)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+		Console.println("" + testSuite.size() + " sequences generated");
+		Console.println("" + currentCoverage + " coverage achieved");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "generateGreedy <modelname> <sequencesName> <minLength> <maxLength> <coverageDepth> <desiredCoverage> {<validEnd>}";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateHybrid.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateHybrid.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateHybrid.java	(revision 922)
@@ -0,0 +1,97 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.testgeneration.HybridGenerator;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to generate sequences of a given length.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDgenerateHybrid implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String sequencesName;
+		int length;
+		int maxLengthAll;
+		int numSequences;
+		boolean validEnd = true;
+		try {
+			modelname = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			length = Integer.parseInt((String) parameters.get(2));
+			maxLengthAll = Integer.parseInt((String) parameters.get(3));
+			numSequences = Integer.parseInt((String) parameters.get(4));
+			if (parameters.size() >= 6) {
+				validEnd = Boolean.parseBoolean((String) parameters.get(5));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		if (length <= maxLengthAll) {
+			// indirectly call command generateFixedLengthSequences
+			List<Object> parameters2 = new LinkedList<Object>();
+			parameters2.add(modelname);
+			parameters2.add(sequencesName);
+			parameters2.add(Integer.toString(length));
+			parameters2.add(Integer.toString(length));
+			parameters2.add(Boolean.toString(false));
+			parameters2.add(Integer.toString(numSequences));
+			parameters2.add(Boolean.toString(validEnd));
+			CMDgenerateFixedLengthSequences cmd = new CMDgenerateFixedLengthSequences();
+			cmd.run(parameters2);
+			return;
+		}
+
+		IStochasticProcess model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		} else if (!(dataObject instanceof IStochasticProcess)) {
+			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
+			return;
+		}
+		model = (IStochasticProcess) dataObject;
+		
+		HybridGenerator generator = new HybridGenerator(numSequences, length, maxLengthAll, validEnd);
+		Collection<List<Event>> sequences = generator.generateTestSuite(model);
+		
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+		Console.println("" + sequences.size() + " sequences generated");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "generateHybrid <modelname> <sequencesName> <lenght> <maxlengthAll> <numSequences> {<validEnd>}";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateRandomSequences.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateRandomSequences.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDgenerateRandomSequences.java	(revision 922)
@@ -0,0 +1,94 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+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.testgeneration.RandomWalkGenerator;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to generate random sessions.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDgenerateRandomSequences implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String sequencesName;
+		int numSessions;
+		int minLength = 0;
+		int maxLength = Integer.MAX_VALUE;
+		long maxIter;
+		boolean validEnd = true;
+		try {
+			modelname = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+			numSessions = Integer.parseInt((String) parameters.get(2));
+			minLength = Integer.parseInt((String) parameters.get(3));
+			maxLength = Integer.parseInt((String) parameters.get(4));
+			maxIter = numSessions * 10;
+			if (parameters.size() >= 5) {
+				maxIter = Long.parseLong((String) parameters.get(5));
+			}
+			if (parameters.size() >= 6) {
+				validEnd = Boolean.parseBoolean((String) parameters.get(6));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		IStochasticProcess model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof IStochasticProcess)) {
+			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
+			return;
+		}
+		model = (IStochasticProcess) dataObject;
+
+		RandomWalkGenerator generator = new RandomWalkGenerator(numSessions,
+				minLength, maxLength, validEnd, maxIter);
+		Collection<List<Event>> sequences = generator
+				.generateTestSuite(model);
+
+		if (sequences.size() < numSessions) {
+			Console.println("Only " + sequences.size()
+					+ " unique sessions generated after " + maxIter
+					+ " iterations");
+		}
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "generateRandomSequenecs <modelname> <sequencesName> <numSequences> <minlength> <maxlength> {<maxIter>} {<validEnd>}";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDlistSymbols.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDlistSymbols.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDlistSymbols.java	(revision 922)
@@ -0,0 +1,72 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.Arrays;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to list all events (symbols) known to a usage profile (stochastic
+ * process).
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDlistSymbols implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname = "";
+		boolean sort = false;
+		try {
+			modelname = (String) parameters.get(0);
+			if (parameters.size() == 2) {
+				sort = Boolean.parseBoolean((String) parameters.get(1));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		IStochasticProcess model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof IStochasticProcess)) {
+			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
+			return;
+		}
+		model = (IStochasticProcess) dataObject;
+		String[] stateStrings = model.getSymbolStrings();
+		if (sort) {
+			Arrays.sort(stateStrings);
+		}
+		for (String stateString : stateStrings) {
+			Console.println(stateString);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "listSymbols <modelname> {<sort>}";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDmodelSize.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDmodelSize.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDmodelSize.java	(revision 922)
@@ -0,0 +1,62 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command that prints the size of a stochastic process to the console.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDmodelSize implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		try {
+			modelname = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof IStochasticProcess)) {
+			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
+			return;
+		}
+
+		IStochasticProcess process = (IStochasticProcess) dataObject;
+		Console.println("#symbols: " + process.getNumSymbols()
+				+ " ; #FOMstates " + process.getNumFOMStates()
+				+ " ; #transitions: " + process.getNumTransitions());
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "modelSize <modelname>";
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDprintDot.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDprintDot.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDprintDot.java	(revision 922)
@@ -0,0 +1,62 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.IDotCompatible;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command that prints a dot representation of a model (if supported) to the
+ * console.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDprintDot implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "printDot <modelname>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname = "";
+		try {
+			modelname = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		IDotCompatible model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof IDotCompatible)) {
+			CommandHelpers.objectNotType(modelname, "IDotCompatible");
+			return;
+		}
+
+		model = (IDotCompatible) dataObject;
+		Console.println(model.getDotRepresentation());
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDprintTrieDot.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDprintTrieDot.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDprintTrieDot.java	(revision 922)
@@ -0,0 +1,63 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.Trie;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command that prints the {@link Trie} of a {@link TrieBasedModel} as dot to
+ * the console.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDprintTrieDot implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "printTreeDot <modelname>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname = "";
+		try {
+			modelname = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		TrieBasedModel model = null;
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof TrieBasedModel)) {
+			CommandHelpers.objectNotType(modelname, "TrieBasedModel");
+			return;
+		}
+
+		model = (TrieBasedModel) dataObject;
+		Console.println(model.getTrieDotRepresentation());
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDshowMarkovModel.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDshowMarkovModel.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDshowMarkovModel.java	(revision 922)
@@ -0,0 +1,112 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.util.List;
+
+import javax.swing.JFrame;
+
+import org.apache.commons.collections15.Transformer;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel.MarkovEdge;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
+import edu.uci.ics.jung.algorithms.layout.Layout;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.visualization.BasicVisualizationServer;
+import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
+import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
+
+/**
+ * <p>
+ * Command that visualizes first-order Markov models.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDshowMarkovModel implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "showMarkovModel <modelname> {<showNodeNames>}";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		boolean showNodeNames = false;
+		try {
+			modelname = (String) parameters.get(0);
+			if (parameters.size() == 2) {
+				showNodeNames = Boolean
+						.parseBoolean((String) parameters.get(1));
+			}
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof FirstOrderMarkovModel)) {
+			CommandHelpers.objectNotType(modelname, "FirstOrderMarkovModel");
+			return;
+		}
+		FirstOrderMarkovModel mm = (FirstOrderMarkovModel) dataObject;
+
+		Graph<String, MarkovEdge> graph = mm.getGraph();
+		Layout<String, MarkovEdge> layout = new ISOMLayout<String, MarkovEdge>(
+				graph);
+		layout.setSize(new Dimension(1000, 800)); // sets the initial size
+													// of the space
+		// The BasicVisualizationServer<V,E> is parameterized by the edge
+		// types
+		BasicVisualizationServer<String, MarkovEdge> vv = new BasicVisualizationServer<String, MarkovEdge>(
+				layout);
+		vv.setPreferredSize(new Dimension(1100, 850)); // Sets the viewing
+														// area size
+
+		if (showNodeNames) {
+			final Rectangle rect = new Rectangle(240, 20);
+
+			Transformer<String, Shape> vertexShapeTransformer = new Transformer<String, Shape>() {
+				public Shape transform(String s) {
+					return rect;
+				}
+			};
+			vv.getRenderer().getVertexLabelRenderer()
+					.setPosition(Position.CNTR);
+			vv.getRenderContext().setVertexShapeTransformer(
+					vertexShapeTransformer);
+			vv.getRenderContext().setVertexLabelTransformer(
+					new ToStringLabeller<String>());
+		}
+
+		vv.getRenderContext().setEdgeLabelTransformer(
+				new ToStringLabeller<MarkovEdge>());
+
+		JFrame frame = new JFrame("Markov Model");
+		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+		frame.getContentPane().add(vv);
+		frame.pack();
+		frame.setVisible(true);
+	}
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDshowTrie.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDshowTrie.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDshowTrie.java	(revision 922)
@@ -0,0 +1,99 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.util.List;
+
+import javax.swing.JFrame;
+
+import org.apache.commons.collections15.Transformer;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.usageprofiles.Trie;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.Edge;
+import de.ugoe.cs.autoquest.usageprofiles.Trie.TrieVertex;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+import edu.uci.ics.jung.algorithms.layout.Layout;
+import edu.uci.ics.jung.algorithms.layout.TreeLayout;
+import edu.uci.ics.jung.graph.Tree;
+import edu.uci.ics.jung.visualization.BasicVisualizationServer;
+import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
+import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
+
+/**
+ * <p>
+ * Command that visualizes the {@link Trie} of a {@link TrieBasedModel}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDshowTrie implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "showTrie <modelname>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		try {
+			modelname = (String) parameters.get(0);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance()
+				.getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof TrieBasedModel)) {
+			CommandHelpers.objectNotType(modelname, "TrieBasedModel");
+		}
+		TrieBasedModel model = (TrieBasedModel) dataObject;
+		Tree<TrieVertex, Edge> graph = model.getTrieGraph();
+		Layout<TrieVertex, Edge> layout = new TreeLayout<TrieVertex, Edge>(
+				graph, 60);
+		// The BasicVisualizationServer<V,E> is parameterized by the edge
+		// types
+		BasicVisualizationServer<TrieVertex, Edge> vv = new BasicVisualizationServer<TrieVertex, Edge>(
+				layout);
+		vv.setPreferredSize(new Dimension(1100, 850)); // Sets the viewing
+														// area size
+
+		final Rectangle rect = new Rectangle(40, 20);
+
+		Transformer<TrieVertex, Shape> vertexShapeTransformer = new Transformer<TrieVertex, Shape>() {
+			public Shape transform(TrieVertex s) {
+				return rect;
+			}
+		};
+		vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
+		vv.getRenderContext().setVertexShapeTransformer(vertexShapeTransformer);
+		vv.getRenderContext().setVertexLabelTransformer(
+				new ToStringLabeller<TrieVertex>());
+
+		JFrame frame = new JFrame("Trie");
+		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+		frame.getContentPane().add(vv);
+		frame.pack();
+		frame.setVisible(true);
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainDFA.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainDFA.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainDFA.java	(revision 922)
@@ -0,0 +1,51 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.usageprofiles.DeterministicFiniteAutomaton;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+
+/**
+ * <p>
+ * Command to train a Deterministic Finite Automaton (DFA).
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 2.0
+ */
+public class CMDtrainDFA extends AbstractTrainCommand {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "trainDFA <modelname> <sequencesName>";
+	}
+
+	/**
+	 * <p>
+	 * No additional parameters.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.autoquest.commands.usage.AbstractTrainCommand#handleAdditionalParameters(java.util.List)
+	 */
+	@Override
+	void handleAdditionalParameters(List<Object> parameters) throws Exception {
+		// no additional parameters.
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.ui.commands.AbstractTrainCommand#createModel()
+	 */
+	@Override
+	TrieBasedModel createModel() {
+		return new DeterministicFiniteAutomaton(new Random());
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainMarkovModel.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainMarkovModel.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainMarkovModel.java	(revision 922)
@@ -0,0 +1,67 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.HighOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+
+/**
+ * <p>
+ * Command to train first-order and high-order Markov models.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 2.0
+ */
+public class CMDtrainMarkovModel extends AbstractTrainCommand {
+
+	/**
+	 * <p>
+	 * Order of the Markov model.
+	 * </p>
+	 */
+	int order;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "trainMarkovModel <modelname> <sequencesName> {<order>}";
+	}
+
+	/**
+	 * <p>
+	 * Handles the parameter order.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.autoquest.commands.usage.AbstractTrainCommand#handleOptionalParameters(java.util.List)
+	 */
+	@Override
+	void handleAdditionalParameters(List<Object> parameters) throws Exception {
+		if (parameters.size() >= 3) {
+			order = Integer.parseInt((String) parameters.get(2));
+		} else {
+			order = 1;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.ui.commands.AbstractTrainCommand#createModel()
+	 */
+	@Override
+	TrieBasedModel createModel() {
+		if (order == 1) {
+			return new FirstOrderMarkovModel(new Random());
+		} else {
+			return new HighOrderMarkovModel(order, new Random());
+		}
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainPPM.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainPPM.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDtrainPPM.java	(revision 922)
@@ -0,0 +1,77 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.autoquest.usageprofiles.PredictionByPartialMatch;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+
+/**
+ * <p>
+ * Command that trains Prediction by Partial Match (PPM) models.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 2.0
+ */
+public class CMDtrainPPM extends AbstractTrainCommand {
+
+	/**
+	 * <p>
+	 * Escape probability of the PPM model.
+	 * </p>
+	 */
+	double probEscape;
+
+	/**
+	 * <p>
+	 * Maximal Markov order of the PPM model.
+	 * </p>
+	 */
+	int maxOrder;
+
+	/**
+	 * <p>
+	 * Minimal Markov order of the PPM model. Default: 0
+	 * </p>
+	 */
+	int minOrder = 0;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "trainPPM <modelname> <sequencesName> <probEscape> <maxOrder> {<minOrder>}";
+	}
+
+	/**
+	 * <p>
+	 * Handles the parameters probEscape, maxOrder, and minOrder.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.autoquest.commands.usage.AbstractTrainCommand#handleOptionalParameters(java.util.List)
+	 */
+	@Override
+	void handleAdditionalParameters(List<Object> parameters) throws Exception {
+		probEscape = Double.parseDouble((String) parameters.get(2));
+		maxOrder = Integer.parseInt((String) parameters.get(3));
+		if (parameters.size() == 5) {
+			minOrder = Integer.parseInt((String) parameters.get(4));
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.autoquest.ui.commands.AbstractTrainCommand#createModel()
+	 */
+	@Override
+	TrieBasedModel createModel() {
+		return new PredictionByPartialMatch(maxOrder, minOrder, new Random(),
+				probEscape);
+	}
+
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDupdateModel.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDupdateModel.java	(revision 922)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usage/CMDupdateModel.java	(revision 922)
@@ -0,0 +1,78 @@
+package de.ugoe.cs.autoquest.commands.usage;
+
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.usageprofiles.TrieBasedModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to update a {@link TrieBasedModel}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDupdateModel implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public void run(List<Object> parameters) {
+		String modelname;
+		String sequencesName;
+
+		try {
+			modelname = (String) parameters.get(0);
+			sequencesName = (String) parameters.get(1);
+		} catch (Exception e) {
+			throw new IllegalArgumentException();
+		}
+
+		Object dataObject = GlobalDataContainer.getInstance().getData(
+				sequencesName);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(sequencesName);
+			return;
+		}
+		if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+			CommandHelpers.objectNotType(sequencesName,
+					"Collection<List<Event<?>>>");
+			return;
+		}
+		Collection<List<Event>> sequences = (Collection<List<Event>>) dataObject;
+
+		dataObject = GlobalDataContainer.getInstance().getData(modelname);
+		if (dataObject == null) {
+			CommandHelpers.objectNotFoundMessage(modelname);
+			return;
+		}
+		if (!(dataObject instanceof TrieBasedModel)) {
+			CommandHelpers.objectNotType(modelname, "TrieBasedModel");
+			return;
+		}
+
+		TrieBasedModel model = (TrieBasedModel) dataObject;
+		model.update(sequences);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public String help() {
+		return "updateModel <modelname> <sequencesName>";
+	}
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/AboutDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/AboutDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/AboutDialog.java	(revision 922)
@@ -0,0 +1,76 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.program.Program;
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.SWT;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+
+public class AboutDialog extends Dialog {
+
+    protected Shell shlAboutEventbenchconsole;
+    private final FormToolkit formToolkit = new FormToolkit(Display.getDefault());
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public AboutDialog(Shell parent, int style) {
+        super(parent, style);
+        setText("SWT Dialog");
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open() {
+        createContents();
+        shlAboutEventbenchconsole.open();
+        shlAboutEventbenchconsole.layout();
+        Display display = getParent().getDisplay();
+        while (!shlAboutEventbenchconsole.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shlAboutEventbenchconsole = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+        shlAboutEventbenchconsole.setSize(283, 113);
+        shlAboutEventbenchconsole.setText("About EventBenchConsole");
+
+        Label lblEventbenchconsole = new Label(shlAboutEventbenchconsole, SWT.CENTER);
+        lblEventbenchconsole.setBounds(10, 10, 267, 15);
+        lblEventbenchconsole.setText("EventBenchConsole");
+
+        Label lblFurtherInformationAbout = new Label(shlAboutEventbenchconsole, SWT.WRAP);
+        lblFurtherInformationAbout.setBounds(10, 31, 267, 31);
+        lblFurtherInformationAbout
+            .setText("Further information about this software is provided on our homepage.");
+
+        final Hyperlink hprlnkHttpeventbenchinformatikunigoettingende =
+            formToolkit.createHyperlink(shlAboutEventbenchconsole,
+                                        "http://eventbench.informatik.uni-goettingen.de", SWT.NONE);
+        hprlnkHttpeventbenchinformatikunigoettingende.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseDown(MouseEvent e) {
+                Program.launch(hprlnkHttpeventbenchinformatikunigoettingende.getText());
+            }
+        });
+        hprlnkHttpeventbenchinformatikunigoettingende.setBounds(10, 68, 267, 17);
+        formToolkit.paintBordersFor(hprlnkHttpeventbenchinformatikunigoettingende);
+        hprlnkHttpeventbenchinformatikunigoettingende.setBackground(null);
+
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/AbstractInsertEventComposite.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/AbstractInsertEventComposite.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/AbstractInsertEventComposite.java	(revision 922)
@@ -0,0 +1,19 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.widgets.Composite;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+
+abstract public class AbstractInsertEventComposite extends Composite {
+
+    protected GUIModel guiModel;
+
+    public AbstractInsertEventComposite(Composite parent, int style, GUIModel guiModel) {
+        super(parent, style);
+        this.guiModel = guiModel;
+    }
+
+    public abstract Event getEvent();
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/CommandHistoryDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/CommandHistoryDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/CommandHistoryDialog.java	(revision 922)
@@ -0,0 +1,185 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.LinkedList;
+import java.util.logging.Level;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.CommandExecuter;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.listener.ICommandListener;
+
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class CommandHistoryDialog extends Dialog implements ICommandListener {
+
+    protected java.util.List<String> history = new LinkedList<String>();
+
+    protected List commandHistoryList;
+    protected Shell shell;
+
+    boolean isOpen;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public CommandHistoryDialog(Shell parent, int style) {
+        super(parent, style);
+        setText("Command History");
+        isOpen = false;
+        Console.getInstance().registerCommandListener(this);
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open() {
+        createContents();
+        shell.open();
+        shell.layout();
+        isOpen = true;
+        Display display = getParent().getDisplay();
+        while (!shell.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE);
+        shell.addDisposeListener(new DisposeListener() {
+            public void widgetDisposed(DisposeEvent arg0) {
+                isOpen = false;
+            }
+        });
+        shell.setSize(450, 300);
+        shell.setText(getText());
+        shell.setLayout(new GridLayout(3, false));
+
+        Label lblRecentCommands = new Label(shell, SWT.NONE);
+        lblRecentCommands.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+        lblRecentCommands.setText("Recent Commands:");
+        new Label(shell, SWT.NONE);
+
+        commandHistoryList = new List(shell, SWT.BORDER | SWT.V_SCROLL | SWT.MULTI);
+        commandHistoryList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
+        for (String command : history) {
+            commandHistoryList.add(command);
+        }
+
+        Button btnExec = new Button(shell, SWT.NONE);
+        btnExec.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                for (String command : commandHistoryList.getSelection()) {
+                    CommandExecuter.getInstance().exec(command);
+                }
+            }
+        });
+        btnExec.setText("Execute");
+
+        Button btnCopyToClipboard = new Button(shell, SWT.NONE);
+        btnCopyToClipboard.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+                StringSelection content = new StringSelection(getSelectedCommands());
+                clipboard.setContents(content, null);
+            }
+        });
+        btnCopyToClipboard.setText("Copy to Clipboard");
+
+        Button btnCreateBatchfile = new Button(shell, SWT.NONE);
+        btnCreateBatchfile.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent event) {
+                FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
+                String filename = fileDialog.open();
+                if (filename != null) {
+                    File file = new File(filename);
+                    boolean fileCreated;
+                    try {
+                        fileCreated = file.createNewFile();
+                        if (!fileCreated) {
+                            Console.traceln(Level.INFO, "Created batchfile " + filename);
+                        }
+                        else {
+                            Console.traceln(Level.INFO, "Overwrote file " + filename);
+                        }
+                    }
+                    catch (IOException e) {
+                        Console.printerrln("Unable to create file " + filename);
+                        Console.logException(e);
+                    }
+                    OutputStreamWriter writer = null;
+                    try {
+                        writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
+                    }
+                    catch (IOException e) {
+                        Console.printerrln("Unable to open file for writing (read-only file):" +
+                            filename);
+                        Console.logException(e);
+                        return;
+                    }
+                    try {
+                        writer.write(getSelectedCommands());
+                        writer.close();
+                    }
+                    catch (IOException e) {
+                        Console.printerrln("Unable to write to file.");
+                        Console.logException(e);
+                    }
+                }
+            }
+        });
+        btnCreateBatchfile.setText("Create Batchfile");
+
+    }
+
+    @Override
+    public void commandNotification(String command) {
+        history.add(command);
+        if (isOpen) {
+            commandHistoryList.add(command);
+        }
+    }
+
+    public boolean isOpen() {
+        return isOpen;
+    }
+
+    private String getSelectedCommands() {
+        StringBuilder commands = new StringBuilder();
+        for (String command : commandHistoryList.getSelection()) {
+            commands.append(command + StringTools.ENDLINE);
+        }
+        return commands.toString();
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ConsoleTabComposite.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ConsoleTabComposite.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ConsoleTabComposite.java	(revision 922)
@@ -0,0 +1,101 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+
+import de.ugoe.cs.util.console.CommandExecuter;
+
+/**
+ * <p>
+ * Implements the composite for the console tab in the applications main window.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class ConsoleTabComposite extends Composite {
+
+    protected Text textCommand;
+
+    protected StyledText textConsoleOutput;
+
+    /**
+     * Create the composite.
+     * 
+     * @param parent
+     * @param style
+     */
+    public ConsoleTabComposite(Composite parent, int style) {
+        super(parent, style);
+        createContents();
+    }
+
+    private void createContents() {
+        setLayout(new GridLayout(3, false));
+
+        Label lblCommand = new Label(this, SWT.NONE);
+        lblCommand.setText("Command:");
+
+        textCommand = new Text(this, SWT.BORDER);
+        textCommand.addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyReleased(KeyEvent e) {
+                if (e.keyCode == SWT.CR) {
+                    executeCommand();
+                }
+            }
+        });
+        GridData gd_textCommand = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+        gd_textCommand.widthHint = 304;
+        textCommand.setLayoutData(gd_textCommand);
+        textCommand.setFocus();
+
+        Button btnEnter = new Button(this, SWT.NONE);
+        btnEnter.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                executeCommand();
+            }
+        });
+        btnEnter.setText("Enter");
+
+        textConsoleOutput =
+            new StyledText(this, SWT.BORDER | SWT.READ_ONLY | SWT.H_SCROLL | SWT.V_SCROLL |
+                SWT.CANCEL);
+        GridData gd_textConsoleOutput = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+        gd_textConsoleOutput.heightHint = 102;
+        gd_textConsoleOutput.widthHint = 456;
+        textConsoleOutput.setLayoutData(gd_textConsoleOutput);
+        textConsoleOutput.addListener(SWT.Modify, new Listener() {
+            public void handleEvent(Event e) {
+                textConsoleOutput.setTopIndex(textConsoleOutput.getLineCount() - 1);
+            }
+        });
+
+    }
+
+    private void executeCommand() {
+        String command = textCommand.getText().trim();
+        CommandExecuter.getInstance().exec(command);
+        textCommand.setText("");
+    }
+
+    @Override
+    protected void checkSubclass() {
+        // Disable the check that prevents subclassing of SWT components
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/DataTabComposite.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/DataTabComposite.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/DataTabComposite.java	(revision 922)
@@ -0,0 +1,117 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.CommandExecuter;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class DataTabComposite extends Composite {
+
+    List dataList;
+
+    /**
+     * Create the composite.
+     * 
+     * @param parent
+     * @param style
+     */
+    public DataTabComposite(Composite parent, int style) {
+        super(parent, style);
+        createContent();
+    }
+
+    private void createContent() {
+        setLayout(new GridLayout(3, false));
+
+        dataList = new List(this, SWT.BORDER | SWT.V_SCROLL);
+        dataList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
+
+        Button btnLoad = new Button(this, SWT.NONE);
+        btnLoad.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                GetObjectNameDialog getObjectNameDialog =
+                    new GetObjectNameDialog(getShell(), SWT.NONE);
+                getObjectNameDialog.open();
+                String objectName = getObjectNameDialog.getObjectName();
+                if ("".equals(objectName)) {
+                    return;
+                }
+                FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+                String filename = fileDialog.open();
+                if (filename == null) {
+                    return;
+                }
+                String command = "loadObject " + filename + " " + objectName;
+                CommandExecuter.getInstance().exec(command);
+                updateDataList();
+            }
+        });
+        btnLoad.setText("Load");
+
+        Button btnSave = new Button(this, SWT.NONE);
+        btnSave.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedStrings = dataList.getSelection();
+                if (selectedStrings.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                    return;
+                }
+                if (selectedStrings.length > 1) {
+                    MessageBox messageBox = new MessageBox(getShell(), SWT.ERROR);
+                    messageBox.setText("Error");
+                    messageBox.setMessage("Only one object storable at a time." +
+                        StringTools.ENDLINE + "Please select only one object.");
+                    return;
+                }
+                FileDialog fileDialog = new FileDialog(getShell(), SWT.SAVE);
+                String filename = fileDialog.open();
+                if (filename == null) {
+                    return;
+                }
+                String command = "saveObject " + filename + " " + selectedStrings[0];
+                CommandExecuter.getInstance().exec(command);
+            }
+        });
+        btnSave.setText("Save");
+
+        Button btnDelete_2 = new Button(this, SWT.NONE);
+        btnDelete_2.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (SWTHelpers.deleteSelectedFromStorage(dataList)) {
+                    updateDataList();
+                }
+                else {
+                    SWTHelpers.noSelectionError(getShell());
+                }
+            }
+        });
+        btnDelete_2.setText("Delete");
+    }
+
+    @Override
+    protected void checkSubclass() {
+        // Disable the check that prevents subclassing of SWT components
+    }
+
+    public void updateDataList() {
+        dataList.removeAll();
+        for (String key : GlobalDataContainer.getInstance().getAllKeys()) {
+            dataList.add(key + " (" + GlobalDataContainer.getInstance().getData(key).getClass().toString() + ")");
+        }
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/EditSequenceDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/EditSequenceDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/EditSequenceDialog.java	(revision 922)
@@ -0,0 +1,209 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+
+public class EditSequenceDialog extends Dialog {
+
+    protected Shell shell;
+    private Table table;
+    private TableColumn tblclmnEventIndex;
+    private TableColumn tblclmnEventType;
+    private TableColumn tblclmnEventTarget;
+
+    private java.util.List<Event> sequence;
+    private GUIModel guiModel;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public EditSequenceDialog(Shell parent, int style, GUIModel guiModel) {
+        super(parent, style);
+        setText("SWT Dialog");
+        this.guiModel = guiModel;
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open(java.util.List<Event> sequence) {
+        this.sequence = sequence;
+        createContents();
+        shell.open();
+        shell.layout();
+        Display display = getParent().getDisplay();
+        while (!shell.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shell = new Shell(getParent(), SWT.SHELL_TRIM | SWT.BORDER | SWT.APPLICATION_MODAL);
+        shell.setSize(450, 300);
+        shell.setText(getText());
+        shell.setLayout(new GridLayout(3, false));
+
+        table = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
+        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
+        table.setHeaderVisible(true);
+        table.setLinesVisible(true);
+        
+        tblclmnEventIndex = new TableColumn(table, SWT.NONE);
+        tblclmnEventIndex.setAlignment(20);
+        
+
+        tblclmnEventType = new TableColumn(table, SWT.NONE);
+        tblclmnEventType.setWidth(100);
+        tblclmnEventType.setText("Event Type");
+
+        tblclmnEventTarget = new TableColumn(table, SWT.NONE);
+        tblclmnEventTarget.setWidth(100);
+        tblclmnEventTarget.setText("Event Target");
+
+        // this listener makes the table entries multiline
+        Listener paintListener = new Listener() {
+            public void handleEvent(org.eclipse.swt.widgets.Event event) {
+                switch (event.type)
+                {
+                    case SWT.MeasureItem: {
+                        TableItem item = (TableItem) event.item;
+                        String text = item.getText(event.index);
+                        Point size = event.gc.textExtent(text);
+                        event.width = size.x;
+                        event.height = Math.max(event.height, size.y);
+                        break;
+                    }
+                    case SWT.PaintItem: {
+                        TableItem item = (TableItem) event.item;
+                        String text = item.getText(event.index);
+                        Point size = event.gc.textExtent(text);
+                        int offset =
+                            event.index == 0 ? Math.max(0, (event.height - size.y) / 2) : 0;
+                        event.gc.drawText(text, event.x, event.y + offset, true);
+                        break;
+                    }
+                    case SWT.EraseItem:
+                        event.detail &= ~SWT.FOREGROUND;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+        };
+        table.addListener(SWT.MeasureItem, paintListener);
+        table.addListener(SWT.PaintItem, paintListener);
+        table.addListener(SWT.EraseItem, paintListener);
+
+        updateTableContents();
+
+        Button btnInsertBefore = new Button(shell, SWT.NONE);
+        btnInsertBefore.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                int index = table.getSelectionIndex();
+                if (index == -1) {
+                    MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+                    messageBox.setMessage("No event selected!");
+                    messageBox.setText("Error");
+                    messageBox.open();
+                }
+                else {
+                    openInsertDialog(index);
+                }
+            }
+        });
+        btnInsertBefore.setText("Insert Before");
+
+        Button btnInsertAfter = new Button(shell, SWT.NONE);
+        btnInsertAfter.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                int index = table.getSelectionIndex();
+                if (index == -1) {
+                    MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+                    messageBox.setMessage("No event selected!");
+                    messageBox.setText("Error");
+                    messageBox.open();
+                }
+                else {
+                    openInsertDialog(index + 1);
+                }
+            }
+        });
+        btnInsertAfter.setText("Insert After");
+
+        Button btnClose = new Button(shell, SWT.NONE);
+        btnClose.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shell.dispose();
+            }
+        });
+        btnClose.setText("Close");
+
+    }
+
+    private void updateTableContents() {
+        table.removeAll();
+        int index = 1;
+        for (Event event : sequence) {
+            TableItem tableItem = new TableItem(table, SWT.NONE);
+            String target = null;
+            if( event.getTarget()!=null ) {
+                target = event.getTarget().toString();
+            }
+            if (target != null) {
+                // the target is split into multiple lines, as one line may
+                // only be 259 characters in tables with Windows
+                target = target.replace("].", "].\n");
+            }
+            tableItem.setText(new String[]
+                { ""+(index++), event.getType().toString(), target });
+        }
+        for (int i = 0; i < table.getColumnCount(); i++) {
+            table.getColumn(i).pack();
+        }
+    }
+
+    private void openInsertDialog(int position) {
+        if (guiModel == null) {
+            MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+            messageBox.setMessage("Operation not supported!\nOnly works for GUI sequences.");
+            messageBox.setText("Error");
+            messageBox.open();
+        } else {
+            InsertAssertionDialog insertDialog = new InsertAssertionDialog(shell, SWT.NONE, guiModel);
+            Event event = insertDialog.open();
+            if (event != null) {
+                sequence.add(position, event);
+                updateTableContents();
+            }
+        }
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GenerateSequencesDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GenerateSequencesDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GenerateSequencesDialog.java	(revision 922)
@@ -0,0 +1,214 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Spinner;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+
+import de.ugoe.cs.util.console.CommandExecuter;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class GenerateSequencesDialog extends Dialog {
+
+    private String processName;
+
+    protected Button btnNumberAll;
+    protected Button btnLengthAll;
+    protected Spinner numberSpinner;
+    protected Spinner iterationsSpinner;
+    protected Spinner minLengthSpinner;
+    protected Spinner maxLengthSpinner;
+
+    protected Shell shlGenerateSequences;
+    private Text sequencesNameText;
+    private final FormToolkit formToolkit = new FormToolkit(Display.getDefault());
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public GenerateSequencesDialog(Shell parent, int style) {
+        super(parent, style);
+        setText("SWT Dialog");
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open() {
+        createContents();
+        shlGenerateSequences.open();
+        shlGenerateSequences.layout();
+        Display display = getParent().getDisplay();
+        while (!shlGenerateSequences.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shlGenerateSequences = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+        shlGenerateSequences.setSize(201, 303);
+        shlGenerateSequences.setText("Generate Sequences");
+        shlGenerateSequences.setLayout(new GridLayout(2, false));
+
+        Group group = new Group(shlGenerateSequences, SWT.NONE);
+        group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+        group.setText("Name");
+        group.setLayout(new GridLayout(1, false));
+
+        sequencesNameText = new Text(group, SWT.BORDER);
+        sequencesNameText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+        Group grpNumber = new Group(shlGenerateSequences, SWT.NONE);
+        grpNumber.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+        grpNumber.setText("Number");
+        grpNumber.setLayout(new GridLayout(2, false));
+
+        numberSpinner = new Spinner(grpNumber, SWT.BORDER);
+        numberSpinner.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+        numberSpinner.setMinimum(1);
+        numberSpinner.setMaximum(Integer.MAX_VALUE);
+
+        btnNumberAll = new Button(grpNumber, SWT.CHECK);
+        btnNumberAll.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                numberSpinner.setEnabled(!btnNumberAll.getSelection());
+                btnLengthAll.setEnabled(!btnNumberAll.getSelection());
+                iterationsSpinner.setEnabled(!btnNumberAll.getSelection());
+            }
+        });
+        btnNumberAll.setText("All");
+
+        Group grpIterations = new Group(shlGenerateSequences, SWT.NONE);
+        grpIterations.setLayout(new GridLayout(1, false));
+        grpIterations.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+        grpIterations.setText("Iterations");
+
+        iterationsSpinner = new Spinner(grpIterations, SWT.BORDER);
+        iterationsSpinner.setMinimum(1);
+        iterationsSpinner.setMaximum(Integer.MAX_VALUE);
+        iterationsSpinner.setSelection(100000);
+        iterationsSpinner.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+        Group grpLength = new Group(shlGenerateSequences, SWT.NONE);
+        grpLength.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+        grpLength.setText("Length");
+        grpLength.setLayout(new GridLayout(4, false));
+
+        btnLengthAll = new Button(grpLength, SWT.CHECK);
+        btnLengthAll.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                minLengthSpinner.setEnabled(!btnLengthAll.getSelection());
+                maxLengthSpinner.setEnabled(!btnLengthAll.getSelection());
+            }
+        });
+        btnLengthAll.setText("All");
+        new Label(grpLength, SWT.NONE);
+        new Label(grpLength, SWT.NONE);
+        new Label(grpLength, SWT.NONE);
+
+        Label label = new Label(grpLength, SWT.NONE);
+        label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+        label.setText("Min.");
+
+        minLengthSpinner = new Spinner(grpLength, SWT.BORDER);
+        minLengthSpinner.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+        minLengthSpinner.setMinimum(1);
+
+        Label label_1 = new Label(grpLength, SWT.NONE);
+        label_1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, 1, 1));
+        label_1.setText("Max.");
+
+        maxLengthSpinner = new Spinner(grpLength, SWT.BORDER);
+        maxLengthSpinner.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+        maxLengthSpinner.setToolTipText("0 means no limitations");
+        maxLengthSpinner.setMinimum(1);
+
+        Button btnGenerate = formToolkit.createButton(shlGenerateSequences, "Generate!", SWT.NONE);
+        btnGenerate.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if ("".equals(sequencesNameText.getText())) {
+                    MessageBox messageBox = new MessageBox(shlGenerateSequences, SWT.ERROR);
+                    messageBox.setText("Error");
+                    messageBox.setMessage("Sequences name not defined!");
+                    messageBox.open();
+                    return;
+                }
+                String sequencesName = sequencesNameText.getText();
+                int number = numberSpinner.getSelection();
+                int minLength = minLengthSpinner.getSelection();
+                int maxLength = maxLengthSpinner.getSelection();
+                int maxIter = iterationsSpinner.getSelection();
+                if (maxIter <= number) {
+                    maxIter = number;
+                }
+                String command = "";
+                if (btnNumberAll.getSelection()) {
+                    if (minLength > maxLength) {
+                        MessageBox messageBox = new MessageBox(shlGenerateSequences, SWT.ERROR);
+                        messageBox.setText("Error");
+                        messageBox
+                            .setMessage("Min. length must be smaller than or equal to max. length!");
+                        messageBox.open();
+                        return;
+                    }
+                    command =
+                        "generateFixedLengthSequences " + processName + " " + sequencesName + " " +
+                            minLength + " " + maxLength + " true";
+                }
+                else {
+                    command =
+                        "generateRandomSequences " + processName + " " + sequencesName + " " +
+                            number + " " + maxIter;
+                    if (!btnLengthAll.getSelection()) {
+                        if (minLength > maxLength) {
+                            MessageBox messageBox = new MessageBox(shlGenerateSequences, SWT.ERROR);
+                            messageBox.setText("Error");
+                            messageBox
+                                .setMessage("Min. length must be smaller than or equal to max. length!");
+                            messageBox.open();
+                            return;
+                        }
+                        command += " " + minLength + " " + maxLength;
+                    }
+                }
+                CommandExecuter.getInstance().exec(command);
+                shlGenerateSequences.dispose();
+            }
+        });
+
+        Button btnAbort = formToolkit.createButton(shlGenerateSequences, "Abort", SWT.NONE);
+        btnAbort.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shlGenerateSequences.dispose();
+            }
+        });
+
+    }
+
+    public void setProcessName(String name) {
+        this.processName = name;
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GetObjectNameDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GetObjectNameDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GetObjectNameDialog.java	(revision 922)
@@ -0,0 +1,97 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class GetObjectNameDialog extends Dialog {
+
+    String objectName = "";
+
+    protected Shell shlObjectName;
+    private Text text;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public GetObjectNameDialog(Shell parent, int style) {
+        super(parent, style);
+        setText("SWT Dialog");
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open() {
+        createContents();
+        shlObjectName.open();
+        shlObjectName.layout();
+        Display display = getParent().getDisplay();
+        while (!shlObjectName.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shlObjectName = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+        shlObjectName.setSize(171, 109);
+        shlObjectName.setText("Object Name");
+        shlObjectName.setLayout(new GridLayout(2, false));
+
+        Label lblPleaseEnterThe = new Label(shlObjectName, SWT.NONE);
+        lblPleaseEnterThe.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+        lblPleaseEnterThe.setText("Please enter the object name:");
+
+        text = new Text(shlObjectName, SWT.BORDER);
+        text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+
+        Button btnOk = new Button(shlObjectName, SWT.NONE);
+        btnOk.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (text.getText().equals("")) {
+                    MessageBox messageBox = new MessageBox(shlObjectName, SWT.ERROR);
+                    messageBox.setText("Error");
+                    messageBox.setMessage("No name entered!");
+                    return;
+                }
+                objectName = text.getText();
+                shlObjectName.dispose();
+            }
+        });
+        btnOk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+        btnOk.setText("Ok");
+
+        Button btnAbort = new Button(shlObjectName, SWT.NONE);
+        btnAbort.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shlObjectName.dispose();
+            }
+        });
+        btnAbort.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+        btnAbort.setText("Abort");
+
+    }
+
+    public String getObjectName() {
+        return objectName;
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GuiModelTabComposite.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GuiModelTabComposite.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/GuiModelTabComposite.java	(revision 922)
@@ -0,0 +1,94 @@
+
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.List;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @version $Revision: $ $Date: Aug 28, 2012$
+ * @author 2012, last modified by $Author: sherbold$
+ */
+public class GuiModelTabComposite extends Composite {
+
+    List guiModelList;
+
+    /**
+     * Create the composite.
+     * 
+     * @param parent
+     * @param style
+     */
+    public GuiModelTabComposite(Composite parent, int style) {
+        super(parent, style);
+        createContents();
+    }
+
+    private void createContents() {
+        setLayout(new GridLayout(5, false));
+
+        guiModelList = new List(this, SWT.BORDER | SWT.V_SCROLL);
+        guiModelList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
+
+        Button btnShow = new Button(this, SWT.NONE);
+        btnShow.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // TODO
+                String[] selectedStrings = guiModelList.getSelection();
+                if (selectedStrings.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                    return;
+                }
+                String modelName = selectedStrings[0];
+                GUIModel model = (GUIModel) GlobalDataContainer.getInstance().getData(modelName);
+
+                ShowGuiModelDialog showGuiModelDialog =
+                    new ShowGuiModelDialog(getShell(), SWT.NONE, model, modelName);
+                showGuiModelDialog.open();
+            }
+        });
+        btnShow.setText("Show");
+
+        Button btnDelete_1 = new Button(this, SWT.NONE);
+        btnDelete_1.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (SWTHelpers.deleteSelectedFromStorage(guiModelList)) {
+                    updateModelList();
+                }
+                else {
+                    SWTHelpers.noSelectionError(getShell());
+                }
+            }
+        });
+        btnDelete_1.setText("Delete");
+    }
+
+    @Override
+    protected void checkSubclass() {
+        // Disable the check that prevents subclassing of SWT components
+    }
+
+    public void updateModelList() {
+        guiModelList.removeAll();
+        for(String key : GlobalDataContainer.getInstance().getAllKeys()) {
+            if( GlobalDataContainer.getInstance().getData(key) instanceof GUIModel ) {
+                guiModelList.add(key);
+            }
+        }
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertAssertionDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertAssertionDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertAssertionDialog.java	(revision 922)
@@ -0,0 +1,109 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+
+public class InsertAssertionDialog extends Dialog {
+
+    protected Event result;
+    protected Shell shell;
+
+    private TabFolder tabFolder;
+
+    List<AbstractInsertEventComposite> insertEventComposites;
+    GUIModel guiModel;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public InsertAssertionDialog(Shell parent, int style, GUIModel guiModel) {
+        super(parent, style);
+        setText("SWT Dialog");
+        this.guiModel = guiModel;
+    }
+
+    /**
+     * Open the dialog.
+     * 
+     * @return the result
+     */
+    public Event open() {
+        result = null;
+        insertEventComposites = new ArrayList<AbstractInsertEventComposite>();
+        createContents();
+        shell.open();
+        shell.layout();
+        Display display = getParent().getDisplay();
+        while (!shell.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shell = new Shell(getParent(), SWT.SHELL_TRIM | SWT.BORDER | SWT.APPLICATION_MODAL);
+        shell.setSize(450, 300);
+        shell.setText(getText());
+        shell.setLayout(new GridLayout(2, false));
+
+        tabFolder = new TabFolder(shell, SWT.NONE);
+        tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+
+        TabItem tbtmTextEquals = new TabItem(tabFolder, SWT.NONE);
+        tbtmTextEquals.setText("TextEquals");
+        AbstractInsertEventComposite compTextEquals =
+            new InsertTextEquals(tabFolder, SWT.NO_BACKGROUND, guiModel);
+        tbtmTextEquals.setControl(compTextEquals);
+        insertEventComposites.add(compTextEquals);
+
+        TabItem tbtmFileEquals = new TabItem(tabFolder, SWT.NONE);
+        tbtmFileEquals.setText("FileEquals");
+        AbstractInsertEventComposite compFileEquals =
+            new InsertFileEquals(tabFolder, SWT.NO_BACKGROUND, guiModel);
+        tbtmFileEquals.setControl(compFileEquals);
+        insertEventComposites.add(compFileEquals);
+
+        Button btnInsert = new Button(shell, SWT.NONE);
+        btnInsert.setText("Insert");
+        btnInsert.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                int index = tabFolder.getSelectionIndex();
+                result = insertEventComposites.get(index).getEvent();
+                shell.dispose();
+            }
+        });
+
+        Button btnAbort = new Button(shell, SWT.NONE);
+        btnAbort.setText("Abort");
+        btnAbort.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shell.dispose();
+            }
+        });
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertFileEquals.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertFileEquals.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertFileEquals.java	(revision 922)
@@ -0,0 +1,83 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+
+import de.ugoe.cs.autoquest.assertions.FileEqualsAssertEventType;
+import de.ugoe.cs.autoquest.assertions.FileEqualsReplay;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class InsertFileEquals extends AbstractInsertEventComposite {
+    private Text actualText;
+    private Text expectedText;
+
+    public InsertFileEquals(Composite parent, int style) {
+        this(parent, style, null);
+    }
+
+    /**
+     * Create the composite.
+     * 
+     * @param parent
+     * @param style
+     */
+    public InsertFileEquals(Composite parent, int style, GUIModel guiModel) {
+        super(parent, style, guiModel);
+        setLayout(new GridLayout(3, false));
+
+        Label lblExpectedFile = new Label(this, SWT.NONE);
+        lblExpectedFile.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+        lblExpectedFile.setText("Expected file:");
+
+        expectedText = new Text(this, SWT.BORDER);
+        expectedText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+        Button btnSearch = new Button(this, SWT.NONE);
+        btnSearch.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+                String filename = fileDialog.open();
+                if (filename != null) {
+                    expectedText.setText(filename);
+                }
+            }
+        });
+        btnSearch.setText("Search...");
+
+        Label lblActualFile = new Label(this, SWT.NONE);
+
+        lblActualFile.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+        lblActualFile.setText("Actual file:");
+
+        actualText = new Text(this, SWT.BORDER);
+        actualText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+        new Label(this, SWT.NONE);
+
+    }
+
+    @Override
+    protected void checkSubclass() {
+        // Disable the check that prevents subclassing of SWT components
+    }
+
+    @Override
+    public Event getEvent() {
+        FileEqualsReplay replay =
+            new FileEqualsReplay(expectedText.getText(), actualText.getText());
+        Event event = new Event(new FileEqualsAssertEventType());
+        event.addReplayable(replay);
+        return event;
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertTextEquals.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertTextEquals.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/InsertTextEquals.java	(revision 922)
@@ -0,0 +1,129 @@
+
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.util.List;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.TreeItem;
+
+import de.ugoe.cs.autoquest.assertions.TextEqualsAssertEventType;
+import de.ugoe.cs.autoquest.assertions.TextEqualsReplay;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class InsertTextEquals extends AbstractInsertEventComposite {
+    private Text expectedText;
+    private Tree guiTree;
+
+    /**
+     * Create the composite.
+     * 
+     * @param parent
+     * @param style
+     */
+    public InsertTextEquals(Composite parent, int style, GUIModel guiModel) {
+        super(parent, style, guiModel);
+        setLayout(new GridLayout(3, false));
+
+        Label lblExpectedValue = new Label(this, SWT.NONE);
+        lblExpectedValue.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+        lblExpectedValue.setText("Expected Value:");
+
+        expectedText = new Text(this, SWT.BORDER);
+        expectedText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+
+        guiTree = new Tree(this, SWT.BORDER);
+        guiTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+        buildGuiTree();
+        new Label(this, SWT.NONE);
+
+        Button btnExpandAll = new Button(this, SWT.NONE);
+        btnExpandAll.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                expandAll(guiTree, true);
+            }
+        });
+        btnExpandAll.setText("Expand all");
+
+        Button btnCollapseAll = new Button(this, SWT.NONE);
+        btnCollapseAll.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                expandAll(guiTree, false);
+            }
+        });
+        btnCollapseAll.setText("Collapse all");
+
+    }
+
+    @Override
+    protected void checkSubclass() {
+        // Disable the check that prevents subclassing of SWT components
+    }
+
+    @Override
+    public Event getEvent() {
+        // TODO possibly display error if no target is selected
+        TreeItem[] selection = guiTree.getSelection();
+        IEventTarget target = null;
+        TextEqualsReplay replay = null;
+        if (selection.length == 1) {
+            target = (IEventTarget) selection[0].getData();
+            replay = new TextEqualsReplay(expectedText.getText(), target.toString());
+        }
+
+        Event event = new Event(new TextEqualsAssertEventType(), target);
+
+        event.setTarget(target);
+        if (replay != null) {
+            event.addReplayable(replay);
+        }
+
+        return event;
+    }
+
+    private void buildGuiTree() {
+        for (IGUIElement element : guiModel.getRootElements()) {
+            TreeItem child = new TreeItem(guiTree, SWT.NULL);
+            child.setText(element.toString());
+            child.setData(element);
+            buildGuiTree(child, guiModel.getChildren(element));
+        }
+    }
+
+    private void buildGuiTree(TreeItem currentParent, List<IGUIElement> elements) {
+        for (IGUIElement element : elements) {
+            TreeItem child = new TreeItem(currentParent, SWT.NULL);
+            child.setText(element.toString());
+            child.setData(element);
+            buildGuiTree(child, guiModel.getChildren(element));
+        }
+    }
+
+    private void expandAll(Tree tree, boolean expanded) {
+        for (TreeItem item : tree.getItems()) {
+            expandAll(item, expanded);
+        }
+    }
+
+    private void expandAll(TreeItem item, boolean expanded) {
+        item.setExpanded(expanded);
+        for (TreeItem childItem : item.getItems()) {
+            expandAll(childItem, expanded);
+        }
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/MainWindow.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/MainWindow.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/MainWindow.java	(revision 922)
@@ -0,0 +1,246 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.util.List;
+import java.util.logging.Level;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+import de.ugoe.cs.util.console.CommandExecuter;
+
+/**
+ * <p>
+ * Main window of the SWT GUI.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class MainWindow {
+
+    private List<String> startupCommands;
+    
+    private final Level traceLevel;
+
+    protected Shell shlEventbenchConsole;
+
+    protected TabItem consoleTab;
+    protected TabItem sequencesTab;
+    protected TabItem modelsTab;
+    protected TabItem guiModelsTab;
+    protected TabItem dataTab;
+    protected ConsoleTabComposite consoleTabComposite;
+    protected SequencesTabComposite sequencesTabComposite;
+    protected ModelsTabComposite modelsTabComposite;
+    protected GuiModelTabComposite guiModelTabComposite;
+    protected DataTabComposite dataTabComposite;
+
+    protected CommandHistoryDialog historyDialog;
+
+    public MainWindow(List<String> startupCommands, Level traceLevel) {
+        this.startupCommands = startupCommands;
+        this.traceLevel = traceLevel;
+    }
+
+    /**
+     * <p>
+     * Open the window.
+     * </p>
+     * 
+     * @wbp.parser.entryPoint
+     */
+    public void open() {
+        Display display = Display.getDefault();
+        createContents();
+        new SWTConsole(consoleTabComposite.textConsoleOutput, traceLevel);
+        historyDialog = new CommandHistoryDialog(shlEventbenchConsole, SWT.NONE);
+        shlEventbenchConsole.open();
+        shlEventbenchConsole.layout();
+        for (String command : startupCommands) {
+            CommandExecuter.getInstance().exec(command);
+        }
+        while (!shlEventbenchConsole.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Create contents of the window.
+     * </p>
+     */
+    protected void createContents() {
+        shlEventbenchConsole = new Shell();
+        shlEventbenchConsole.setSize(800, 600);
+        shlEventbenchConsole.setText("EventBench Console");
+        shlEventbenchConsole.setLayout(new GridLayout(1, false));
+
+        createMenu();
+        createTabs();
+    }
+
+    /**
+     * </p> Creates the menu of the window. </p>
+     */
+    private void createMenu() {
+        Menu menu = new Menu(shlEventbenchConsole, SWT.BAR);
+        shlEventbenchConsole.setMenuBar(menu);
+
+        MenuItem mntmFile = new MenuItem(menu, SWT.CASCADE);
+        mntmFile.setText("File");
+
+        Menu menu_1 = new Menu(mntmFile);
+        mntmFile.setMenu(menu_1);
+
+        MenuItem mntmShowHistory = new MenuItem(menu_1, SWT.NONE);
+        mntmShowHistory.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (!historyDialog.isOpen()) {
+                    historyDialog.open();
+                }
+            }
+        });
+        mntmShowHistory.setText("Show History");
+
+        MenuItem mntmExecBatchFile = new MenuItem(menu_1, SWT.NONE);
+        mntmExecBatchFile.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                FileDialog fileDialog = new FileDialog(shlEventbenchConsole, SWT.OPEN);
+                String filename = fileDialog.open();
+                if (filename != null) {
+                    String command = "exec '" + filename + "'";
+                    CommandExecuter.getInstance().exec(command);
+                }
+            }
+        });
+        mntmExecBatchFile.setText("Exec. Batch File");
+
+        new MenuItem(menu_1, SWT.SEPARATOR);
+
+        MenuItem mntmLoad = new MenuItem(menu_1, SWT.NONE);
+        mntmLoad.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                FileDialog fileDialog = new FileDialog(shlEventbenchConsole, SWT.OPEN);
+                String filename = fileDialog.open();
+                if (filename != null) {
+                    String command = "load '" + filename + "'";
+                    CommandExecuter.getInstance().exec(command);
+                }
+            }
+        });
+        mntmLoad.setText("Load...");
+
+        MenuItem mntmSave = new MenuItem(menu_1, SWT.NONE);
+        mntmSave.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                FileDialog fileDialog = new FileDialog(shlEventbenchConsole, SWT.SAVE);
+                String filename = fileDialog.open();
+                if (filename != null) {
+                    String command = "save '" + filename + "'";
+                    CommandExecuter.getInstance().exec(command);
+                }
+            }
+        });
+        mntmSave.setText("Save...");
+
+        new MenuItem(menu_1, SWT.SEPARATOR);
+
+        MenuItem mntmExit = new MenuItem(menu_1, SWT.NONE);
+        mntmExit.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shlEventbenchConsole.dispose();
+            }
+        });
+        mntmExit.setText("Exit");
+
+        MenuItem mntmHelp = new MenuItem(menu, SWT.CASCADE);
+        mntmHelp.setText("Help");
+
+        Menu menu_2 = new Menu(mntmHelp);
+        mntmHelp.setMenu(menu_2);
+
+        MenuItem mntmAbout = new MenuItem(menu_2, SWT.NONE);
+        mntmAbout.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                AboutDialog aboutDialog = new AboutDialog(shlEventbenchConsole, SWT.NONE);
+                aboutDialog.open();
+            }
+        });
+        mntmAbout.setText("About");
+    }
+
+    /**
+     * <p>
+     * Creates the central TabFolder of the window.
+     * </p>
+     */
+    private void createTabs() {
+        TabFolder tabFolder = new TabFolder(shlEventbenchConsole, SWT.NONE);
+        tabFolder.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (e.item == sequencesTab) {
+                    sequencesTabComposite.updateSequenceList();
+                }
+                else if (e.item == modelsTab) {
+                    modelsTabComposite.updateModelList();
+                }
+                else if (e.item == guiModelsTab) {
+                    guiModelTabComposite.updateModelList();
+                }
+                else if (e.item == dataTab) {
+                    dataTabComposite.updateDataList();
+                }
+            }
+        });
+        tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+        consoleTab = new TabItem(tabFolder, SWT.NONE);
+        consoleTab.setText("Console");
+
+        consoleTabComposite = new ConsoleTabComposite(tabFolder, SWT.NO_BACKGROUND);
+        consoleTab.setControl(consoleTabComposite);
+
+        sequencesTab = new TabItem(tabFolder, SWT.NONE);
+        sequencesTab.setText("Sequences");
+
+        sequencesTabComposite = new SequencesTabComposite(tabFolder, SWT.NO_BACKGROUND);
+        sequencesTab.setControl(sequencesTabComposite);
+
+        modelsTab = new TabItem(tabFolder, SWT.NONE);
+        modelsTab.setText("Models");
+
+        modelsTabComposite = new ModelsTabComposite(tabFolder, SWT.NO_BACKGROUND);
+        modelsTab.setControl(modelsTabComposite);
+        
+        guiModelsTab = new TabItem(tabFolder, SWT.NONE);
+        guiModelsTab.setText("GUI Models");
+
+        guiModelTabComposite = new GuiModelTabComposite(tabFolder, SWT.NO_BACKGROUND);
+        guiModelsTab.setControl(guiModelTabComposite);
+
+        dataTab = new TabItem(tabFolder, SWT.NONE);
+        dataTab.setText("Data");
+
+        dataTabComposite = new DataTabComposite(tabFolder, SWT.NO_BACKGROUND);
+        dataTab.setControl(dataTabComposite);
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ModelPropertiesDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ModelPropertiesDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ModelPropertiesDialog.java	(revision 922)
@@ -0,0 +1,142 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.FillLayout;
+
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class ModelPropertiesDialog extends Dialog {
+
+    private IStochasticProcess process;
+
+    protected Shell shlModelProperties;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public ModelPropertiesDialog(Shell parent, int style) {
+        super(parent, style);
+        setText("SWT Dialog");
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open() {
+        createContents();
+        shlModelProperties.open();
+        shlModelProperties.layout();
+        Display display = getParent().getDisplay();
+        while (!shlModelProperties.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shlModelProperties = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE);
+        shlModelProperties.setSize(230, 318);
+        shlModelProperties.setText("Model Properties");
+        shlModelProperties.setLayout(new GridLayout(2, false));
+
+        Group grpEvents = new Group(shlModelProperties, SWT.NONE);
+        FillLayout fl_grpEvents = new FillLayout(SWT.HORIZONTAL);
+        fl_grpEvents.marginHeight = 5;
+        fl_grpEvents.marginWidth = 5;
+        grpEvents.setLayout(fl_grpEvents);
+        grpEvents.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+        grpEvents.setText("Events");
+
+        List list = new List(grpEvents, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+        for (String symbol : process.getSymbolStrings()) {
+            if (symbol == null) {
+                list.add("null");
+            }
+            else {
+                list.add(symbol);
+            }
+        }
+
+        Group grpStatistics = new Group(shlModelProperties, SWT.NONE);
+        grpStatistics.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+        grpStatistics.setText("Statistics");
+        grpStatistics.setLayout(new GridLayout(2, false));
+
+        Label lblNumEvents = new Label(grpStatistics, SWT.NONE);
+        lblNumEvents.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+        lblNumEvents.setText("Num. Events");
+
+        Label label = new Label(grpStatistics, SWT.RIGHT);
+        label.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1));
+        label.setText("" + process.getNumSymbols());
+
+        Label lblNumTrieLeafs = new Label(grpStatistics, SWT.NONE);
+        lblNumTrieLeafs.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+        lblNumTrieLeafs.setText("Size (Flattend FOM)");
+
+        Label label_1 = new Label(grpStatistics, SWT.RIGHT);
+        label_1.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1));
+        label_1.setText("" + process.getNumFOMStates());
+
+        Label lblEntropy = new Label(grpStatistics, SWT.NONE);
+        lblEntropy.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+        lblEntropy.setText("Entropy");
+
+        final Label label_2 = new Label(grpStatistics, SWT.RIGHT);
+        label_2.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+        label_2.setText("####");
+
+        Button btnCalculateEntropy = new Button(shlModelProperties, SWT.NONE);
+        btnCalculateEntropy.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (process instanceof FirstOrderMarkovModel) {
+                    label_2.setText("" + ((FirstOrderMarkovModel) process).calcEntropy());
+                }
+                else {
+                    MessageBox messageBox = new MessageBox(shlModelProperties, SWT.NONE);
+                    messageBox.setText("Feature Not Available");
+                    messageBox
+                        .setMessage("The feature is currently only available for first-order Markov models.");
+                    messageBox.open();
+                }
+            }
+        });
+        btnCalculateEntropy.setText("Calculate Entropy");
+
+        Button btnClose = new Button(shlModelProperties, SWT.NONE);
+        btnClose.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shlModelProperties.dispose();
+            }
+        });
+        btnClose.setText("Close");
+
+    }
+
+    public void setStochasticProcess(IStochasticProcess process) {
+        this.process = process;
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ModelsTabComposite.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ModelsTabComposite.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ModelsTabComposite.java	(revision 922)
@@ -0,0 +1,159 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+
+import de.ugoe.cs.autoquest.usageprofiles.FirstOrderMarkovModel;
+import de.ugoe.cs.autoquest.usageprofiles.IDotCompatible;
+import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
+import de.ugoe.cs.util.console.CommandExecuter;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class ModelsTabComposite extends Composite {
+
+    List modelList;
+
+    /**
+     * Create the composite.
+     * 
+     * @param parent
+     * @param style
+     */
+    public ModelsTabComposite(Composite parent, int style) {
+        super(parent, style);
+        createContents();
+    }
+
+    private void createContents() {
+        setLayout(new GridLayout(5, false));
+
+        modelList = new List(this, SWT.BORDER | SWT.V_SCROLL);
+        modelList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
+
+        Button btnShow = new Button(this, SWT.NONE);
+        btnShow.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedStrings = modelList.getSelection();
+                if (selectedStrings.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                    return;
+                }
+                IStochasticProcess process =
+                    (IStochasticProcess) GlobalDataContainer.getInstance()
+                        .getData(selectedStrings[0]);
+                if (process instanceof FirstOrderMarkovModel) {
+                    String command = "showMarkovModel " + selectedStrings[0];
+                    CommandExecuter.getInstance().exec(command);
+                }
+                else {
+                    MessageBox messageBox = new MessageBox(getShell(), SWT.NONE);
+                    messageBox.setText("Feature Not Available");
+                    messageBox
+                        .setMessage("The feature is currently only available for first-order Markov models.");
+                    messageBox.open();
+                }
+            }
+        });
+        btnShow.setText("Visualize");
+
+        Button btnDelete_1 = new Button(this, SWT.NONE);
+        btnDelete_1.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (SWTHelpers.deleteSelectedFromStorage(modelList)) {
+                    updateModelList();
+                }
+                else {
+                    SWTHelpers.noSelectionError(getShell());
+                }
+            }
+        });
+        btnDelete_1.setText("Delete");
+
+        Button btnGenSequences = new Button(this, SWT.NONE);
+        btnGenSequences.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedStrings = modelList.getSelection();
+                if (selectedStrings.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                    return;
+                }
+                GenerateSequencesDialog generateSequencesDialog =
+                    new GenerateSequencesDialog(getShell(), SWT.NONE);
+                generateSequencesDialog.setProcessName(selectedStrings[0]);
+                generateSequencesDialog.open();
+            }
+        });
+        btnGenSequences.setText("Gen. Sequences");
+
+        Button btnProperties = new Button(this, SWT.NONE);
+        btnProperties.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedStrings = modelList.getSelection();
+                if (selectedStrings.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                    return;
+                }
+                IStochasticProcess process =
+                    (IStochasticProcess) GlobalDataContainer.getInstance()
+                        .getData(selectedStrings[0]);
+                ModelPropertiesDialog modelPropertiesDialog =
+                    new ModelPropertiesDialog(getShell(), SWT.NONE);
+                modelPropertiesDialog.setStochasticProcess(process);
+                modelPropertiesDialog.open();
+            }
+        });
+        btnProperties.setText("Properties");
+
+        Button btnCreateDot = new Button(this, SWT.NONE);
+        btnCreateDot.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedStrings = modelList.getSelection();
+                if (selectedStrings.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                    return;
+                }
+                IStochasticProcess process =
+                    (IStochasticProcess) GlobalDataContainer.getInstance()
+                        .getData(selectedStrings[0]);
+                String command = "";
+                if (process instanceof IDotCompatible) {
+                    command = "printDot ";
+                }
+                else {
+                    command = "printTrieDot ";
+                }
+                command += selectedStrings[0];
+                CommandExecuter.getInstance().exec(command);
+            }
+        });
+        btnCreateDot.setText("Create DOT");
+    }
+
+    @Override
+    protected void checkSubclass() {
+        // Disable the check that prevents subclassing of SWT components
+    }
+
+    public void updateModelList() {
+        modelList.removeAll();
+        for(String key : GlobalDataContainer.getInstance().getAllKeys()) {
+            if( GlobalDataContainer.getInstance().getData(key) instanceof IStochasticProcess ) {
+                modelList.add(key);
+            }
+        }
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SWTConsole.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SWTConsole.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SWTConsole.java	(revision 922)
@@ -0,0 +1,97 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.logging.Level;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.MessageBox;
+
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.listener.ICommandListener;
+import de.ugoe.cs.util.console.listener.IErrorListener;
+import de.ugoe.cs.util.console.listener.IExceptionListener;
+import de.ugoe.cs.util.console.listener.IOutputListener;
+import de.ugoe.cs.util.console.listener.ITraceListener;
+
+public class SWTConsole implements IOutputListener, IErrorListener, ITraceListener,
+    ICommandListener, IExceptionListener
+{
+
+    private StyledText output;
+    
+    private Level traceLevel;
+
+    public SWTConsole(StyledText styledText, Level traceLevel) {
+        Console.getInstance().registerOutputListener(this);
+        Console.getInstance().registerErrorListener(this);
+        Console.getInstance().registerTraceListener(this);
+        Console.getInstance().registerCommandListener(this);
+        Console.getInstance().registerExceptionListener(this);
+        this.output = styledText;
+        this.traceLevel = traceLevel;
+    }
+
+    @Override
+    public void outputMsg(String newMessage) {
+        output.append(newMessage);
+    }
+
+    @Override
+    public void errorMsg(String errMessage) {
+        appendColored("[ERROR] " + errMessage, SWT.COLOR_RED);
+    }
+
+    @Override
+    public void traceMsg(String traceMessage, Level level) {
+        if( level.intValue()>=traceLevel.intValue()) {
+            int color = SWT.COLOR_BLUE;
+            if( level==Level.SEVERE ) {
+                color = SWT.COLOR_RED;
+            }
+            appendColored("[" + level.toString() + "] " + traceMessage, color);
+        }
+    }
+
+    @Override
+    public void commandNotification(String command) {
+        output.append("> " + command + StringTools.ENDLINE);
+    }
+
+    private void appendColored(String str, int id) {
+        StyleRange styleRange = new StyleRange();
+        styleRange.start = output.getText().length();
+        styleRange.length = str.length();
+        styleRange.foreground = output.getDisplay().getSystemColor(id);
+        output.append(str);
+        output.setStyleRange(styleRange);
+    }
+
+    @Override
+    public void logException(Exception e) {
+        MessageBox messageBox = new MessageBox(output.getShell(), SWT.ERROR);
+        messageBox.setText("Error");
+        messageBox.setMessage(e.getMessage());
+        messageBox.open();
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        e.printStackTrace(ps);        
+        String stackTrace = null;
+        try {
+            stackTrace = baos.toString("UTF-8");
+        }
+        catch (UnsupportedEncodingException e1) {
+        }
+        if( stackTrace!=null ) {
+            appendColored(stackTrace, SWT.COLOR_RED);
+        } else {
+            appendColored(e.getMessage(), SWT.COLOR_RED);
+        }
+        
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SWTHelpers.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SWTHelpers.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SWTHelpers.java	(revision 922)
@@ -0,0 +1,33 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+
+import de.ugoe.cs.util.console.CommandExecuter;
+
+public class SWTHelpers {
+
+    public static boolean deleteSelectedFromStorage(final List list) {
+        String[] selectedStrings = list.getSelection();
+        if (selectedStrings.length == 0) {
+            return false;
+        }
+        else {
+            for (String selected : selectedStrings) {
+                String command = "deleteObject " + selected;
+                CommandExecuter.getInstance().exec(command);
+            }
+            return true;
+        }
+    }
+
+    public static void noSelectionError(final Shell shell) {
+        MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+        messageBox.setMessage("No objects selected!");
+        messageBox.setText("Error");
+        messageBox.open();
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SequencesDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SequencesDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SequencesDialog.java	(revision 922)
@@ -0,0 +1,144 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.util.Collection;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.layout.GridData;
+
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class SequencesDialog extends Dialog {
+
+    private String sequencesName;
+
+    private List sequenceList;
+    private Collection<java.util.List<Event>> sequences;
+    private GUIModel guiModel;
+
+    protected Shell shell;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public SequencesDialog(Shell parent, int style) {
+        super(parent, style);
+        setText("SWT Dialog");
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open(String sequencesName) {
+        this.sequencesName = sequencesName;
+        sequences = null;
+        createContents();
+        shell.open();
+        shell.layout();
+        Display display = getParent().getDisplay();
+        while (!shell.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shell = new Shell(getParent(), SWT.SHELL_TRIM | SWT.BORDER | SWT.APPLICATION_MODAL);
+        shell.setSize(248, 299);
+        shell.setText(getText());
+        shell.setLayout(new GridLayout(2, false));
+
+        sequenceList = new List(shell, SWT.BORDER | SWT.V_SCROLL);
+        sequenceList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+        updateSequenceList();
+
+        Button btnShow = new Button(shell, SWT.NONE);
+        btnShow.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                int index = sequenceList.getSelectionIndex();
+                if (index == -1) {
+                    MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+                    messageBox.setMessage("No sequence selected!");
+                    messageBox.setText("Error");
+                    messageBox.open();
+                }
+                else {
+                    EditSequenceDialog editSequenceDialog =
+                        new EditSequenceDialog(shell, SWT.NONE, guiModel);
+                    int counter = 0;
+                    java.util.List<Event> selectedSequence = null;
+                    for (java.util.List<Event> sequence : sequences) {
+                        if (counter == index) {
+                            selectedSequence = sequence;
+                            break;
+                        }
+                        counter++;
+                    }
+                    editSequenceDialog.open(selectedSequence);
+                    updateSequenceList();
+                }
+            }
+        });
+        btnShow.setText("Show");
+
+        Button btnClose = new Button(shell, SWT.NONE);
+        btnClose.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shell.dispose();
+            }
+        });
+        btnClose.setText("Close");
+
+    }
+
+    @SuppressWarnings("unchecked")
+    private void updateSequenceList() {
+        sequenceList.removeAll();
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            sequences = (Collection<java.util.List<Event>>) dataObject;
+            int seqDigits = Integer.toString(sequences.size()).length();
+            int counter = 1;
+            for (java.util.List<Event> sequence : sequences) {
+                String seqName =
+                    "#" + String.format("%0" + seqDigits + "d", counter) + ": " + sequence.size();
+                sequenceList.add(seqName);
+                counter++;
+            }
+            Object targetObject =
+                GlobalDataContainer.getInstance().getData(sequencesName + "_targets");
+            guiModel = null;
+            if (targetObject instanceof GUIModel) {
+                guiModel = (GUIModel) targetObject;
+            }
+        }
+        else {
+            MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+            messageBox.setMessage("Internal error. Sequences object not of expected type!");
+            messageBox.setText("Error");
+            messageBox.open();
+        }
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SequencesTabComposite.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SequencesTabComposite.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/SequencesTabComposite.java	(revision 922)
@@ -0,0 +1,144 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import org.eclipse.swt.widgets.Composite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.util.console.CommandExecuter;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+public class SequencesTabComposite extends Composite {
+
+    protected List sequenceList;
+
+    /**
+     * Create the composite.
+     * 
+     * @param parent
+     * @param style
+     */
+    public SequencesTabComposite(Composite parent, int style) {
+        super(parent, style);
+        createContents();
+    }
+
+    private void createContents() {
+        setLayout(new GridLayout(5, false));
+
+        sequenceList = new List(this, SWT.BORDER | SWT.V_SCROLL | SWT.MULTI);
+        sequenceList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
+        sequenceList.setItems(new String[] { });
+
+        Button btnEdit = new Button(this, SWT.NONE);
+        btnEdit.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedSequences = sequenceList.getSelection();
+                if (selectedSequences.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                }
+                else if (selectedSequences.length > 1) {
+                    MessageBox messageBox = new MessageBox(getShell(), SWT.ERROR);
+                    messageBox.setMessage("Only one sequence can be edited at a time!");
+                    messageBox.setText("Error");
+                    messageBox.open();
+                }
+                else {
+                    SequencesDialog sequencesDialog = new SequencesDialog(getShell(), SWT.NONE);
+                    sequencesDialog.open(selectedSequences[0]);
+                }
+            }
+        });
+        btnEdit.setText("Edit");
+
+        Button btnDelete = new Button(this, SWT.NONE);
+        btnDelete.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (SWTHelpers.deleteSelectedFromStorage(sequenceList)) {
+                    updateSequenceList();
+                }
+                else {
+                    SWTHelpers.noSelectionError(getShell());
+                }
+            }
+        });
+        btnDelete.setText("Delete");
+
+        Button btnReplay = new Button(this, SWT.NONE);
+        btnReplay.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedSequences = sequenceList.getSelection();
+                if (selectedSequences.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                }
+                else {
+                    StringBuilder commandString = new StringBuilder("generateReplayfile ");
+                    FileDialog fileDialog = new FileDialog(getShell());
+                    String filename = fileDialog.open();
+                    commandString.append(filename + " ");
+                    for (String selected : selectedSequences) {
+                        commandString.append(selected + " ");
+                    }
+                    CommandExecuter.getInstance().exec(commandString.toString().trim());
+                }
+            }
+        });
+        btnReplay.setText("Replay");
+
+        Button btnTrainModel = new Button(this, SWT.NONE);
+        btnTrainModel.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String[] selectedSequences = sequenceList.getSelection();
+                if (selectedSequences.length == 0) {
+                    SWTHelpers.noSelectionError(getShell());
+                }
+                else {
+                    TrainModelDialog trainDialog = new TrainModelDialog(getShell(), SWT.NONE);
+                    trainDialog.setSequenceNames(selectedSequences);
+                    trainDialog.open();
+                }
+            }
+        });
+        btnTrainModel.setText("Train Model");
+
+        Button btnParse = new Button(this, SWT.NONE);
+        btnParse.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // TODO implement parsing of sequences
+                MessageBox messageBox = new MessageBox(getShell(), SWT.ICON_INFORMATION);
+                messageBox.setText("Not implemented!");
+                messageBox.setMessage("Sorry! This functionality has not been implemented yet!");
+                messageBox.open();
+            }
+        });
+        btnParse.setText("Parse");
+    }
+
+    public void updateSequenceList() {
+        sequenceList.removeAll();
+        for(String key : GlobalDataContainer.getInstance().getAllKeys()) {
+            if( SequenceInstanceOf.isCollectionOfSequences(GlobalDataContainer.getInstance().getData(key)) ) {
+                sequenceList.add(key);
+            }
+        }
+    }
+
+    @Override
+    protected void checkSubclass() {
+        // Disable the check that prevents subclassing of SWT components
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ShowGuiModelDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ShowGuiModelDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ShowGuiModelDialog.java	(revision 922)
@@ -0,0 +1,175 @@
+
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.util.console.Console;
+
+import org.eclipse.swt.widgets.Label;
+
+public class ShowGuiModelDialog extends Dialog {
+
+    protected Shell shell;
+    private Tree guiTree;
+
+    protected GUIModel model;
+
+    public ShowGuiModelDialog(Shell parent, int style, GUIModel model, String modelName) {
+        super(parent, style);
+        setText("GUI Model " + modelName);
+        this.model = model;
+    }
+
+    public void open() {
+        createContents();
+        shell.open();
+        shell.layout();
+        Display display = getParent().getDisplay();
+        while (!shell.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    private void createContents() {
+        shell = new Shell(getParent(), SWT.SHELL_TRIM | SWT.BORDER | SWT.APPLICATION_MODAL);
+        shell.setSize(450, 300);
+        shell.setText(getText());
+
+        shell.setLayout(new GridLayout(4, false));
+
+        guiTree = new Tree(shell, SWT.BORDER | SWT.MULTI);
+        guiTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
+
+        buildGuiTree();
+
+        Button btnExpandAll = new Button(shell, SWT.NONE);
+        btnExpandAll.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                expandAll(guiTree, true);
+            }
+        });
+        btnExpandAll.setText("Expand all");
+
+        Button btnCollapseAll = new Button(shell, SWT.NONE);
+        btnCollapseAll.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                expandAll(guiTree, false);
+            }
+        });
+        btnCollapseAll.setText("Collapse all");
+        
+        Button btnCondense = new Button(shell, SWT.NONE);
+        btnCondense.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                model.condenseModel();
+                guiTree.removeAll();
+                buildGuiTree();
+            }
+        });
+        btnCondense.setText("Condense");
+        
+        Button btnMerge = new Button(shell, SWT.NONE);
+        btnMerge.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                mergeSelectedNode(guiTree);
+            }
+        });
+        btnMerge.setText("Merge nodes");
+        
+        //new Label(shell, SWT.NONE);
+        new Label(shell, SWT.NONE);
+        new Label(shell, SWT.NONE);
+        new Label(shell, SWT.NONE);
+
+    }
+
+    private void buildGuiTree() {
+        for (IGUIElement element : model.getRootElements()) {
+            TreeItem child = new TreeItem(guiTree, SWT.NULL);
+            child.setText(element.toString());
+            child.setData(element);
+            buildGuiTree(child, model.getChildren(element));
+        }
+    }
+
+    private void buildGuiTree(TreeItem currentParent, List<IGUIElement> elements) {
+        for (IGUIElement element : elements) {
+            TreeItem child = new TreeItem(currentParent, SWT.NULL);
+            child.setText(element.toString());
+            child.setData(element);
+            buildGuiTree(child, model.getChildren(element));
+        }
+    }
+
+    private void expandAll(Tree tree, boolean expanded) {
+        for (TreeItem item : tree.getItems()) {
+            expandAll(item, expanded);
+        }
+    }
+
+    private void expandAll(TreeItem item, boolean expanded) {
+        item.setExpanded(expanded);
+        for (TreeItem childItem : item.getItems()) {
+            expandAll(childItem, expanded);
+        }
+    }
+    
+    private void mergeSelectedNode(Tree tree) {
+        TreeItem[] selectedNodes = tree.getSelection();
+        if( selectedNodes.length<2 ) {
+            MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+            messageBox.setMessage("Must select at least two nodes to merge!");
+            messageBox.setText("Error");
+            messageBox.open();
+            return;
+        }
+        
+        TreeItem firstParent = selectedNodes[0].getParentItem();
+        for( int i=1 ; i<selectedNodes.length ; i++ ) {
+            if( firstParent!=selectedNodes[i].getParentItem() ) {
+                MessageBox messageBox = new MessageBox(shell, SWT.ERROR);
+                messageBox.setMessage("All selected nodes must have the same parent!");
+                messageBox.setText("Error");
+                messageBox.open();
+                return;
+            }
+        }
+        
+        try {
+            // try to merge the elements
+            IGUIElement firstElement = (IGUIElement) selectedNodes[0].getData();
+            for( int i=1 ; i<selectedNodes.length ; i++ ) {
+                model.mergeGUIElements(firstElement, (IGUIElement) selectedNodes[i].getData());
+            }
+        } catch( IllegalArgumentException e) {
+            Console.logException(e);
+        }
+        
+        // update visualization of the model
+        firstParent.removeAll();
+        buildGuiTree(firstParent, model.getChildren((IGUIElement) firstParent.getData()));
+        firstParent.setExpanded(true);
+    }
+
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/TrainModelDialog.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/TrainModelDialog.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/TrainModelDialog.java	(revision 922)
@@ -0,0 +1,238 @@
+package de.ugoe.cs.autoquest.ui.swt;
+
+import java.util.Arrays;
+
+import org.eclipse.swt.widgets.Dialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Spinner;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+import de.ugoe.cs.util.console.CommandExecuter;
+
+public class TrainModelDialog extends Dialog {
+
+    protected Shell shlTrainUsageModel;
+
+    private Button btnFirstorderMarkovModel;
+    private Button btnHighorderMarkovModel;
+    private Button btnPredictionByPartial;
+    private Button btnDeterministicFiniteAutomaton;
+
+    private Spinner minOrderSpinner;
+    private Spinner maxOrderSpinner;
+    private Text modelnameText;
+
+    private String[] sequenceNames;
+    private Text probEscapeText;
+    private Label lblEscapeProbability;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parent
+     * @param style
+     */
+    public TrainModelDialog(Shell parent, int style) {
+        super(parent, style);
+        setText("SWT Dialog");
+    }
+
+    /**
+     * Open the dialog.
+     */
+    public void open() {
+        createContents();
+        shlTrainUsageModel.open();
+        shlTrainUsageModel.layout();
+        Display display = getParent().getDisplay();
+        while (!shlTrainUsageModel.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+    }
+
+    /**
+     * Create contents of the dialog.
+     */
+    private void createContents() {
+        shlTrainUsageModel =
+            new Shell(getParent(), SWT.DIALOG_TRIM | SWT.MIN | SWT.APPLICATION_MODAL);
+        shlTrainUsageModel.setSize(219, 330);
+        shlTrainUsageModel.setText("Train Usage Model");
+        shlTrainUsageModel.setLayout(new GridLayout(2, false));
+
+        Group grpGeneralInformation = new Group(shlTrainUsageModel, SWT.NONE);
+        grpGeneralInformation.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1));
+        grpGeneralInformation.setText("Name");
+        grpGeneralInformation.setLayout(new GridLayout(1, false));
+
+        modelnameText = new Text(grpGeneralInformation, SWT.BORDER);
+        modelnameText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
+
+        Group grpType = new Group(shlTrainUsageModel, SWT.NONE);
+        grpType.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1));
+        grpType.setText("Type");
+        grpType.setLayout(new GridLayout(2, false));
+
+        btnFirstorderMarkovModel = new Button(grpType, SWT.RADIO);
+        btnFirstorderMarkovModel.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                minOrderSpinner.setEnabled(false);
+                maxOrderSpinner.setEnabled(false);
+                probEscapeText.setEnabled(false);
+            }
+        });
+        btnFirstorderMarkovModel
+            .setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, false, 2, 1));
+        btnFirstorderMarkovModel.setText("First-Order Markov Model");
+        btnFirstorderMarkovModel.setSelection(true);
+
+        btnHighorderMarkovModel = new Button(grpType, SWT.RADIO);
+        btnHighorderMarkovModel.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                minOrderSpinner.setEnabled(false);
+                maxOrderSpinner.setEnabled(true);
+                probEscapeText.setEnabled(false);
+            }
+        });
+        btnHighorderMarkovModel.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, false, 2, 1));
+        btnHighorderMarkovModel.setText("High-Order Markov Model");
+
+        btnPredictionByPartial = new Button(grpType, SWT.RADIO);
+        btnPredictionByPartial.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                minOrderSpinner.setEnabled(true);
+                maxOrderSpinner.setEnabled(true);
+                probEscapeText.setEnabled(true);
+            }
+        });
+        btnPredictionByPartial.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, true, false, 2, 1));
+        btnPredictionByPartial.setText("Prediction by Partial Match");
+
+        lblEscapeProbability = new Label(grpType, SWT.NONE);
+        lblEscapeProbability.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false, 1, 1));
+        lblEscapeProbability.setText("Escape Probability:");
+
+        probEscapeText = new Text(grpType, SWT.BORDER);
+        probEscapeText.setText("0.1");
+        probEscapeText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+        probEscapeText.setEnabled(false);
+
+        btnDeterministicFiniteAutomaton = new Button(grpType, SWT.RADIO);
+        btnDeterministicFiniteAutomaton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                minOrderSpinner.setEnabled(false);
+                maxOrderSpinner.setEnabled(false);
+                probEscapeText.setEnabled(false);
+            }
+        });
+        btnDeterministicFiniteAutomaton.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false,
+                                                                   false, 2, 1));
+        btnDeterministicFiniteAutomaton.setText("Deterministic Finite Automaton");
+
+        Group grpModelProperties = new Group(shlTrainUsageModel, SWT.NONE);
+        grpModelProperties.setLayout(new GridLayout(4, false));
+        grpModelProperties.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true, 2, 1));
+        grpModelProperties.setText("Order");
+
+        Label lblMin = new Label(grpModelProperties, SWT.NONE);
+        lblMin.setText("Min.");
+
+        minOrderSpinner = new Spinner(grpModelProperties, SWT.BORDER);
+        minOrderSpinner.setEnabled(false);
+
+        Label lblMax = new Label(grpModelProperties, SWT.NONE);
+        lblMax.setText("Max.");
+
+        maxOrderSpinner = new Spinner(grpModelProperties, SWT.BORDER);
+        maxOrderSpinner.setEnabled(false);
+        maxOrderSpinner.setMinimum(2);
+
+        Button btnTrain = new Button(shlTrainUsageModel, SWT.NONE);
+        btnTrain.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                String command = "";
+                String modelname = modelnameText.getText();
+                if (modelname.equals("")) {
+                    MessageBox messageBox = new MessageBox(shlTrainUsageModel, SWT.ERROR);
+                    messageBox.setText("Error");
+                    messageBox.setMessage("No modelname defined!");
+                    messageBox.open();
+                    return;
+                }
+                if (btnFirstorderMarkovModel.getSelection()) {
+                    command = "trainMarkovModel " + modelname + " " + sequenceNames[0];
+
+                }
+                else if (btnHighorderMarkovModel.getSelection()) {
+                    int modelOrder = maxOrderSpinner.getSelection();
+                    command =
+                        "trainMarkovModel " + modelname + " " + sequenceNames[0] + " " + modelOrder;
+                }
+                else if (btnPredictionByPartial.getSelection()) {
+                    int minOrder = minOrderSpinner.getSelection();
+                    int maxOrder = maxOrderSpinner.getSelection();
+                    if (minOrder > maxOrder) {
+                        MessageBox messageBox = new MessageBox(shlTrainUsageModel, SWT.ERROR);
+                        messageBox.setText("Error");
+                        messageBox
+                            .setMessage("Min. Order must be smaller than or equal to max. order!");
+                        messageBox.open();
+                        return;
+                    }
+                    double probEscape = Double.parseDouble(probEscapeText.getText());
+                    if (probEscape < 0.0 || probEscape > 1.0) {
+                        MessageBox messageBox = new MessageBox(shlTrainUsageModel, SWT.ERROR);
+                        messageBox.setText("Error");
+                        messageBox.setMessage("Escape probability must be in [0,1]!");
+                        messageBox.open();
+                        return;
+                    }
+                    command =
+                        "trainPPM " + modelname + " " + sequenceNames[0] + " " + probEscape + " " +
+                            maxOrder + " " + minOrder;
+                }
+                else if (btnDeterministicFiniteAutomaton.getSelection()) {
+                    command = "trainDFA " + modelname + " " + sequenceNames[0];
+                }
+                CommandExecuter.getInstance().exec(command);
+                for (int i = 1; i < sequenceNames.length; i++) {
+                    command = "updateModel " + sequenceNames[i];
+                    CommandExecuter.getInstance().exec(command);
+                }
+                shlTrainUsageModel.dispose();
+            }
+        });
+        btnTrain.setText("Train!");
+
+        Button btnAbort = new Button(shlTrainUsageModel, SWT.NONE);
+        btnAbort.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                shlTrainUsageModel.dispose();
+            }
+        });
+        btnAbort.setText("Abort");
+
+    }
+
+    public void setSequenceNames(String[] sequenceNames) {
+        this.sequenceNames = Arrays.copyOf(sequenceNames, sequenceNames.length);
+    }
+}
Index: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/commands/CMDshowSequences.java
===================================================================
--- trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/commands/CMDshowSequences.java	(revision 922)
+++ trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/commands/CMDshowSequences.java	(revision 922)
@@ -0,0 +1,66 @@
+package de.ugoe.cs.autoquest.ui.swt.commands;
+
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.ui.swt.SequencesDialog;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * Command to show sequences.
+ * </p>
+ * 
+ * @author Jeffrey Hall, Steffen Herbold
+ */
+public class CMDshowSequences implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "showSequences <sequencesName>";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        try {
+            sequencesName = (String) parameters.get(0);
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        Shell shell = new Shell(SWT.NONE);
+        shell.open();
+        shell.layout();
+        shell.setSize(0, 0);
+        SequencesDialog sequencesDialog = new SequencesDialog(shell, SWT.NONE);
+        sequencesDialog.open(sequencesName);
+        shell.dispose();
+    }
+}
Index: trunk/autoquest/.settings/org.eclipse.m2e.core.prefs
===================================================================
--- trunk/autoquest/.settings/org.eclipse.m2e.core.prefs	(revision 921)
+++ trunk/autoquest/.settings/org.eclipse.m2e.core.prefs	(revision 922)
