Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/SequenceInstanceOfTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/SequenceInstanceOfTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/SequenceInstanceOfTest.java	(revision 432)
@@ -0,0 +1,100 @@
+package de.ugoe.cs.quest;
+
+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 de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.data.Event;
+
+
+/**
+ * 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<? extends Event<?>>> sequences = new LinkedList<List<? extends Event<?>>>();
+		List<Event<?>> sequence1 = new ArrayList<Event<?>>();
+		sequence1.add(new Event<String>("a"));
+		sequences.add(sequence1);
+		
+		boolean result = SequenceInstanceOf.isCollectionOfSequences(sequences);
+		assertTrue(result);
+	}
+	
+	@Test
+	public void TestIsCollectionOfSequences_2() throws Exception {
+		Collection<List<? extends Event<?>>> sequences = new LinkedList<List<? extends 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<? extends Event<?>>> sequences = new LinkedList<List<? extends 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<String>("a"));
+		
+		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/quest-core-event-test/src/de/ugoe/cs/quest/TestAll.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/TestAll.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/TestAll.java	(revision 432)
@@ -0,0 +1,29 @@
+package de.ugoe.cs.quest;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * The class <code>TestAll</code> builds a suite that can be used to run all
+ * of the tests within its package as well as within any subpackages of its
+ * package.
+ *
+ * @generatedBy CodePro at 12/15/11 3:35 PM
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+	SequenceInstanceOfTest.class,
+	de.ugoe.cs.quest.assertions.TestAll.class,
+	de.ugoe.cs.quest.coverage.TestAll.class,
+	de.ugoe.cs.quest.data.TestAll.class,
+	de.ugoe.cs.quest.models.TestAll.class
+})
+public class TestAll {
+
+	public static void main(String[] args) {
+		JUnitCore.runClasses(new Class[] { TestAll.class });
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/AssertEventTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/AssertEventTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/AssertEventTest.java	(revision 432)
@@ -0,0 +1,38 @@
+package de.ugoe.cs.quest.assertions;
+
+import org.junit.*;
+
+import de.ugoe.cs.quest.assertions.AssertEvent;
+import de.ugoe.cs.quest.data.mock.MockReplayable;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>AssertEventTest</code> contains tests for the class
+ * <code>{@link AssertEvent}</code>.
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class AssertEventTest {
+
+	@Test
+	public void testAssertEvent_1() throws Exception {
+		String type = "typeString";
+
+		AssertEvent<MockReplayable> result = new AssertEvent<MockReplayable>(
+				type);
+
+		assertNotNull(result);
+		assertEquals(type, result.getType());
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testAssertEvent_2() throws Exception {
+
+		new AssertEvent<MockReplayable>(null);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(AssertEventTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/FileEqualsReplayTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/FileEqualsReplayTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/FileEqualsReplayTest.java	(revision 432)
@@ -0,0 +1,80 @@
+package de.ugoe.cs.quest.assertions;
+
+import org.junit.*;
+
+import de.ugoe.cs.quest.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.security.InvalidParameterException.class)
+	public void testFileEqualsReplay_2() throws Exception {
+		String actualFile = "actualFileString";
+
+		new FileEqualsReplay(null, actualFile);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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);
+	}
+
+	@Test
+	public void testGetTarget_1() throws Exception {
+		FileEqualsReplay fixture = new FileEqualsReplay("", "");
+
+		String result = fixture.getTarget();
+
+		assertEquals("targetNotUsed", result);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(FileEqualsReplayTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/TestAll.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/TestAll.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/TestAll.java	(revision 432)
@@ -0,0 +1,27 @@
+package de.ugoe.cs.quest.assertions;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * The class <code>TestAll</code> builds a suite that can be used to run all
+ * of the tests within its package as well as within any subpackages of its
+ * package.
+ *
+ * @generatedBy CodePro at 12/21/11 11:53 AM
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+	FileEqualsReplayTest.class,
+	TextEqualsReplayTest.class,
+	AssertEventTest.class
+})
+public class TestAll {
+
+	public static void main(String[] args) {
+		JUnitCore.runClasses(new Class[] { TestAll.class });
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/TextEqualsReplayTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/TextEqualsReplayTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/assertions/TextEqualsReplayTest.java	(revision 432)
@@ -0,0 +1,85 @@
+package de.ugoe.cs.quest.assertions;
+
+import org.junit.*;
+
+import de.ugoe.cs.quest.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.security.InvalidParameterException.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);
+	}
+
+	@Test
+	public void testGetTarget_1() throws Exception {
+		TextEqualsReplay fixture = new TextEqualsReplay("expectedValueString",
+				"targetString");
+
+		String result = fixture.getTarget();
+
+		assertEquals("targetString", result);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(TextEqualsReplayTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/CoverageCalculatorObservedTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/CoverageCalculatorObservedTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/CoverageCalculatorObservedTest.java	(revision 432)
@@ -0,0 +1,313 @@
+package de.ugoe.cs.quest.coverage;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import de.ugoe.cs.quest.coverage.CoverageCalculatorObserved;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends Event<?>>> sequencesObserved;
+	
+	Set<List<? extends Event<?>>> sequencesCovered;
+	Set<List<? extends Event<?>>> sequencesCovered2;
+	Set<List<? extends 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.security.InvalidParameterException.class)
+	public void testCoverageCalculatorObserved_2() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorObserved(null,sequencesCovered, length);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testCoverageCalculatorObserved_3() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorObserved(sequencesObserved, null, length);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testCoverageCalculatorObserved_4() throws Exception {
+		int length = 0;
+
+		new CoverageCalculatorObserved(sequencesObserved, sequencesCovered, length);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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<? extends Event<?>>>();
+		List<Event<?>> sequence1 = new ArrayList<Event<?>>();
+		sequence1.add(new Event<String>("a"));
+		sequence1.add(new Event<String>("b"));
+		sequence1.add(new Event<String>("r"));
+		sequence1.add(new Event<String>("a"));
+		List<Event<?>> sequence2 = new ArrayList<Event<?>>();
+		sequence2.add(new Event<String>("c"));
+		sequence2.add(new Event<String>("a"));
+		sequence2.add(new Event<String>("d"));
+		sequence2.add(new Event<String>("a"));
+		sequence2.add(new Event<String>("b"));
+		sequence2.add(new Event<String>("r"));
+		sequence2.add(new Event<String>("a"));
+		sequencesObserved.add(sequence1);
+		sequencesObserved.add(sequence2);
+
+		sequencesCovered = new LinkedHashSet<List<? extends Event<?>>>();
+		List<Event<?>> tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("b"));
+		tmpList.add(new Event<String>("r"));
+		sequencesCovered.add(tmpList);
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("b"));
+		tmpList.add(new Event<String>("r"));
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("e"));
+		sequencesCovered.add(tmpList);
+
+		sequencesCovered2 = new LinkedHashSet<List<? extends Event<?>>>();
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("b"));
+		tmpList.add(new Event<String>("r"));
+		sequencesCovered2.add(tmpList);
+		
+		sequencesNewPossible = new LinkedHashSet<List<? extends Event<?>>>();
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("r"));
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("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/quest-core-event-test/src/de/ugoe/cs/quest/coverage/CoverageCalculatorProcessTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/CoverageCalculatorProcessTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/CoverageCalculatorProcessTest.java	(revision 432)
@@ -0,0 +1,236 @@
+package de.ugoe.cs.quest.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.quest.coverage.CoverageCalculatorProcess;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends Event<?>>> sequencesCovered;
+	Set<List<? extends 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.security.InvalidParameterException.class)
+	public void testCoverageCalculatorProcess_2() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorProcess(null,sequencesCovered, length);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testCoverageCalculatorProcess_3() throws Exception {
+		int length = 2;
+
+		new CoverageCalculatorProcess(mockProcess, null, length);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testCoverageCalculatorProcess_4() throws Exception {
+		int length = 0;
+
+		new CoverageCalculatorProcess(mockProcess, sequencesCovered, length);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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.security.InvalidParameterException.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<? extends Event<?>>> sequences = new LinkedList<List<? extends Event<?>>>();
+		List<Event<?>> sequence1 = new ArrayList<Event<?>>();
+		sequence1.add(new Event<String>("a"));
+		sequence1.add(new Event<String>("b"));
+		sequence1.add(new Event<String>("r"));
+		sequence1.add(new Event<String>("a"));
+		List<Event<?>> sequence2 = new ArrayList<Event<?>>();
+		sequence2.add(new Event<String>("c"));
+		sequence2.add(new Event<String>("a"));
+		sequence2.add(new Event<String>("d"));
+		sequence2.add(new Event<String>("a"));
+		sequence2.add(new Event<String>("b"));
+		sequence2.add(new Event<String>("r"));
+		sequence2.add(new Event<String>("a"));
+		sequences.add(sequence1);
+		sequences.add(sequence2);
+
+		sequencesCovered = new LinkedHashSet<List<? extends Event<?>>>();
+		List<Event<?>> tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("b"));
+		tmpList.add(new Event<String>("r"));
+		sequencesCovered.add(tmpList);
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("b"));
+		tmpList.add(new Event<String>("r"));
+		tmpList.add(new Event<String>("a"));
+		sequencesCovered.add(tmpList);
+
+		sequencesCovered2 = new LinkedHashSet<List<? extends Event<?>>>();
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("b"));
+		tmpList.add(new Event<String>("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/quest-core-event-test/src/de/ugoe/cs/quest/coverage/SequenceToolsTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/SequenceToolsTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/SequenceToolsTest.java	(revision 432)
@@ -0,0 +1,201 @@
+package de.ugoe.cs.quest.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.quest.coverage.SequenceTools;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.quest.models.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<? extends Event<?>>> sequences;
+	Set<List<? extends Event<?>>> subSequences;
+	MockTrieBasedModel mockProcess;
+	
+	@Test
+	public void testContainedSubSequences_1()
+		throws Exception {
+		int length = 2;
+
+		Set<List<? extends 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<? extends Event<?>>> result = SequenceTools.containedSubSequences(null, length);
+		assertNotNull(result);
+		assertTrue(result.isEmpty());
+	}
+	
+	@Test(expected=java.security.InvalidParameterException.class)
+	public void testContainedSubSequences_3()
+		throws Exception {
+		int length = 0;
+
+		SequenceTools.containedSubSequences(sequences, length);
+	}
+	
+	@Test(expected=java.security.InvalidParameterException.class)
+	public void testContainedSubSequences_4()
+		throws Exception {
+		int length = -1;
+
+		SequenceTools.containedSubSequences(sequences, length);
+	}
+
+	@Test
+	public void testGenerateWeights_1()
+		throws Exception {
+		Map<List<? extends Event<?>>, Double> result = SequenceTools.generateWeights(mockProcess, subSequences);
+
+		assertNotNull(result);
+		Set<Entry<List<? extends Event<?>>, Double>> entrySet = result.entrySet();
+		assertEquals(subSequences.size(),entrySet.size());
+		for( Entry<List<? extends 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<? extends Event<?>>, Double> result = SequenceTools.generateWeights(null, subSequences);
+		
+		Set<Entry<List<? extends Event<?>>, Double>> entrySet = result.entrySet();
+		assertEquals(subSequences.size(),entrySet.size());
+		for( Entry<List<? extends 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<? extends 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.security.InvalidParameterException.class )
+	public void testNumSequences_3()
+		throws Exception {
+		int length = 0;
+
+		SequenceTools.numSequences(mockProcess, length);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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<? extends Event<?>>>();
+		List<Event<?>> sequence1 = new ArrayList<Event<?>>();
+		sequence1.add(new Event<String>("a"));
+		sequence1.add(new Event<String>("b"));
+		sequence1.add(new Event<String>("r"));
+		sequence1.add(new Event<String>("a"));
+		List<Event<?>> sequence2 = new ArrayList<Event<?>>();
+		sequence2.add(new Event<String>("c"));
+		sequence2.add(new Event<String>("a"));
+		sequence2.add(new Event<String>("d"));
+		sequence2.add(new Event<String>("a"));
+		sequence2.add(new Event<String>("b"));
+		sequence2.add(new Event<String>("r"));
+		sequence2.add(new Event<String>("a"));
+		sequences.add(sequence1);
+		sequences.add(sequence2);
+		
+		subSequences = new LinkedHashSet<List<? extends Event<?>>>();
+		List<Event<?>> tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("b"));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("b"));
+		tmpList.add(new Event<String>("r"));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("r"));
+		tmpList.add(new Event<String>("a"));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("c"));
+		tmpList.add(new Event<String>("a"));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("a"));
+		tmpList.add(new Event<String>("d"));
+		subSequences.add(tmpList);
+		tmpList = new ArrayList<Event<?>>();
+		tmpList.add(new Event<String>("d"));
+		tmpList.add(new Event<String>("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/quest-core-event-test/src/de/ugoe/cs/quest/coverage/TestAll.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/TestAll.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/coverage/TestAll.java	(revision 432)
@@ -0,0 +1,26 @@
+package de.ugoe.cs.quest.coverage;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * The class <code>TestAll</code> builds a suite that can be used to run all
+ * of the tests within its package as well as within any subpackages of its
+ * package.
+ *
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+	CoverageCalculatorObservedTest.class,
+	CoverageCalculatorProcessTest.class,
+	SequenceToolsTest.class
+})
+public class TestAll {
+
+	public static void main(String[] args) {
+		JUnitCore.runClasses(new Class[] { TestAll.class });
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/EventTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/EventTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/EventTest.java	(revision 432)
@@ -0,0 +1,406 @@
+package de.ugoe.cs.quest.data;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+import org.junit.*;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+
+import static org.junit.Assert.*;
+
+/**
+ * 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 {
+		String type = "typeString";
+
+		Event<String> result = new Event<String>(type);
+
+		assertNotNull(result);
+		assertEquals(type, result.type);
+		assertNull(result.target);
+		assertNull(result.targetShort);
+		assertEquals("", result.idInfo);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testEvent_2() throws Exception {
+		new Event<String>(null);
+	}
+
+	@Test
+	public void testEquals_1() throws Exception {
+		String type1 = "typeString";
+		String type2 = "typeString";
+		Event<String> fixture = new Event<String>(type1);
+		Event<String> other = new Event<String>(type2);
+
+		boolean result = fixture.equals(other);
+
+		assertTrue(result);
+	}
+
+	@Test
+	public void testEquals_2() throws Exception {
+		String type1 = "typeString1";
+		String type2 = "typeString2";
+		Event<String> fixture = new Event<String>(type1);
+		Event<String> other = new Event<String>(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<String> fixture = new Event<String>(type1);
+		fixture.target = target1;
+		Event<String> other = new Event<String>(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<String> fixture = new Event<String>(type1);
+		fixture.target = target1;
+		Event<String> other = new Event<String>(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<String> fixture = new Event<String>(type1);
+		fixture.target = target1;
+		Event<String> other = new Event<String>(type2);
+		other.target = target2;
+
+		boolean result = fixture.equals(other);
+
+		assertFalse(result);
+	}
+
+	@Test
+	public void testEquals_6() throws Exception {
+		String type = "typeString";
+		Event<String> fixture = new Event<String>(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 testGetIdInfo_fixture_1() throws Exception {
+		String type = "typeString";
+		String idInfo = "idInfoString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.idInfo = idInfo;
+
+		String result = fixture.getIdInfo();
+
+		assertEquals(idInfo, result);
+	}
+
+	@Test
+	public void testGetShortId_1() throws Exception {
+		String type = "typeString";
+		String targetShort = "targetShortString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.targetShort = targetShort;
+
+		String result = fixture.getShortId();
+
+		assertEquals("targetShortString.typeString", result);
+	}
+
+	@Test
+	public void testGetShortId_2() throws Exception {
+		String type = "typeString";
+		String targetShort = "targetShortString";
+		String idInfo = "idInfoString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.targetShort = targetShort;
+		fixture.idInfo = idInfo;
+
+		String result = fixture.getShortId();
+
+		assertEquals("targetShortString.typeString.idInfoString", result);
+	}
+
+	@Test
+	public void testGetShortId_3() throws Exception {
+		String type = "typeString";
+		String target = "targetString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.target = target;
+
+		String result = fixture.getShortId();
+
+		assertEquals("targetString.typeString", result);
+	}
+
+	@Test
+	public void testGetStandardId_1() throws Exception {
+		String type = "typeString";
+		String target = "targetString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.target = target;
+
+		String result = fixture.getStandardId();
+
+		assertEquals("targetString.typeString", result);
+	}
+
+	@Test
+	public void testGetStandardId_2() throws Exception {
+		String type = "typeString";
+		String target = "targetString";
+		String idInfo = "idInfoString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.target = target;
+		fixture.idInfo = idInfo;
+
+		String result = fixture.getStandardId();
+
+		assertEquals("targetString.typeString.idInfoString", result);
+	}
+
+	@Test
+	public void testGetStandardId_3() throws Exception {
+		String type = "typeString";
+		Event<String> fixture = new Event<String>(type);
+
+		String result = fixture.getStandardId();
+
+		assertEquals("typeString", result);
+	}
+
+	@Test
+	public void testGetStandardId_4() throws Exception {
+		String type = "typeString";
+		String idInfo = "idInfoString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.idInfo = idInfo;
+
+		String result = fixture.getStandardId();
+
+		assertEquals("typeString.idInfoString", result);
+	}
+
+	@Test
+	public void testGetTarget_1() throws Exception {
+		String type = "typeString";
+		String target = "targetString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.target = target;
+
+		String result = fixture.getTarget();
+
+		assertEquals(target, result);
+	}
+
+	@Test
+	public void testGetTarget_2() throws Exception {
+		String type = "typeString";
+		Event<String> fixture = new Event<String>(type);
+
+		String result = fixture.getTarget();
+
+		assertNull(result);
+	}
+
+	@Test
+	public void testGetTargetShort_1() throws Exception {
+		String type = "typeString";
+		String targetShort = "targetShort";
+		Event<String> fixture = new Event<String>(type);
+		fixture.targetShort = targetShort;
+
+		String result = fixture.getTargetShort();
+
+		assertEquals(targetShort, result);
+	}
+
+	@Test
+	public void testGetTargetShort_2() throws Exception {
+		String type = "typeString";
+		Event<String> fixture = new Event<String>(type);
+
+		String result = fixture.getTargetShort();
+
+		assertNull(result);
+	}
+
+	@Test
+	public void testGetType_1() throws Exception {
+		String type = "typeString";
+		Event<String> fixture = new Event<String>(type);
+
+		String result = fixture.getType();
+
+		assertEquals(type, result);
+	}
+
+	@Test
+	public void testSetIdInfo_fixture_1() throws Exception {
+		String type = "typeString";
+		String idInfo = "idInfoString";
+		Event<String> fixture = new Event<String>(type);
+
+		fixture.setIdInfo(idInfo);
+
+		assertEquals(idInfo, fixture.idInfo);
+	}
+
+	@Test
+	public void testSetIdInfo_2() throws Exception {
+		String type = "typeString";
+		String idInfo = null;
+		Event<String> fixture = new Event<String>(type);
+
+		fixture.setIdInfo(idInfo);
+
+		assertEquals(idInfo, fixture.idInfo);
+	}
+
+	@Test
+	public void testSetTarget_1() throws Exception {
+		String type = "typeString";
+		String target = "targetString";
+		Event<String> fixture = new Event<String>(type);
+
+		boolean result = fixture.setTarget(target);
+
+		assertTrue(result);
+		assertEquals(target, fixture.target);
+	}
+
+	@Test
+	public void testSetTarget_2() throws Exception {
+		String type = "typeString";
+		String target1 = "targetString1";
+		String target2 = "targetString2";
+		Event<String> fixture = new Event<String>(type);
+		fixture.target = target1;
+
+		boolean result = fixture.setTarget(target2);
+
+		assertFalse(result);
+		assertEquals(target1, fixture.target);
+	}
+
+	@Test
+	public void testSetTargetShort_1() throws Exception {
+		String type = "typeString";
+		String targetShort = "targetShortString";
+		Event<String> fixture = new Event<String>(type);
+
+		boolean result = fixture.setTargetShort(targetShort);
+
+		assertTrue(result);
+		assertEquals(targetShort, fixture.targetShort);
+	}
+
+	@Test
+	public void testSetTargetShort_2() throws Exception {
+		String type = "typeString";
+		String targetShort1 = "targetShortString1";
+		String targetShort2 = "targetShortString2";
+		Event<String> fixture = new Event<String>(type);
+		fixture.targetShort = targetShort1;
+
+		boolean result = fixture.setTargetShort(targetShort2);
+
+		assertFalse(result);
+		assertEquals(targetShort1, fixture.targetShort);
+	}
+
+	@Test
+	public void testToString_1() throws Exception {
+		String type = "typeString";
+		String target = "targetString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.target = target;
+
+		String result = fixture.toString();
+
+		assertEquals("targetString.typeString", result);
+	}
+
+	@Test
+	public void testToString_2() throws Exception {
+		String type = "typeString";
+		String target = "targetString";
+		String idInfo = "idInfoString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.target = target;
+		fixture.idInfo = idInfo;
+
+		String result = fixture.toString();
+
+		assertEquals("targetString.typeString.idInfoString", result);
+	}
+
+	@Test
+	public void testToString_3() throws Exception {
+		String type = "typeString";
+		Event<String> fixture = new Event<String>(type);
+
+		String result = fixture.toString();
+
+		assertEquals("typeString", result);
+	}
+
+	@Test
+	public void testToString_4() throws Exception {
+		String type = "typeString";
+		String idInfo = "idInfoString";
+		Event<String> fixture = new Event<String>(type);
+		fixture.idInfo = idInfo;
+
+		String result = fixture.toString();
+
+		assertEquals("typeString.idInfoString", result);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(EventTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/ReplayableEventTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/ReplayableEventTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/ReplayableEventTest.java	(revision 432)
@@ -0,0 +1,474 @@
+package de.ugoe.cs.quest.data;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import junitx.framework.ListAssert;
+import de.ugoe.cs.quest.IReplayDecorator;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+import de.ugoe.cs.quest.data.mock.MockReplayable;
+
+import org.junit.*;
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>ReplayableEventTest</code> contains tests for the class
+ * <code>{@link ReplayableEvent}</code>.
+ * 
+ * @generatedBy CodePro at 12/20/11 10:17 AM
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class ReplayableEventTest {
+
+	private static class StubReplayDecorator implements IReplayDecorator {
+
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public String getHeader() {
+			return null;
+		}
+
+		@Override
+		public String getFooter() {
+			return null;
+		}
+
+		@Override
+		public String getSessionHeader(int sessionId) {
+			return null;
+		}
+
+		@Override
+		public String getSessionFooter(int sessionId) {
+			return null;
+		}
+		
+	}
+
+	@Test
+	public void testReplayableEvent_1() throws Exception {
+		String type = "typeString";
+
+		ReplayableEvent<MockReplayable> result = new ReplayableEvent<MockReplayable>(
+				type);
+
+		assertNotNull(result);
+		assertNotNull(result.replayEvents);
+		assertTrue(result.replayEvents.isEmpty());
+		assertEquals(true, result.replayValid);
+		assertEquals(null, result.decorator);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testReplayableEvent_2() throws Exception {
+		new ReplayableEvent<MockReplayable>(null);
+	}
+
+	@Test
+	public void testAddReplayEvent_1() throws Exception {
+		String type = "typeString";
+		String replayableReplay = "replayString";
+		String replaybleTarget = "replayTargetString";
+		MockReplayable replayable = new MockReplayable(replayableReplay,
+				replaybleTarget);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.addReplayEvent(replayable);
+		
+		
+		assertEquals(1, fixture.replayEvents.size());
+		assertEquals(replayable, fixture.replayEvents.get(0));
+	}
+	
+	@Test
+	public void testAddReplayEvent_2() throws Exception {
+		String type = "typeString";
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replaybleTarget1 = "replayTargetString1";
+		String replaybleTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replaybleTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replaybleTarget2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.addReplayEvent(replayable1);
+		fixture.addReplayEvent(replayable2);
+		
+		
+		assertEquals(2, fixture.replayEvents.size());
+		assertEquals(replayable1, fixture.replayEvents.get(0));
+		assertEquals(replayable2, fixture.replayEvents.get(1));
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class )
+	public void testAddReplayEvent_fixture_3() throws Exception {
+		String type = "typeString";
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.addReplayEvent(null);
+	}
+
+	@Test
+	public void testAddReplaySequence_1() throws Exception {
+		String type = "typeString";
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replaybleTarget1 = "replayTargetString1";
+		String replaybleTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replaybleTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replaybleTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		
+		fixture.addReplaySequence(replaySequence);		
+		
+		assertEquals(2, fixture.replayEvents.size());
+		assertEquals(replayable1, fixture.replayEvents.get(0));
+		assertEquals(replayable2, fixture.replayEvents.get(1));
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class )
+	public void testAddReplaySequence_2() throws Exception {
+		String type = "typeString";
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		
+		fixture.addReplaySequence(null);	
+	}
+
+	@Test
+	public void testEquals_1() throws Exception {
+		String type = "typeString";
+		boolean replayValid = true;
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replayableTarget1 = "replayTargetString1";
+		String replayableTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replayableTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replayableTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayEvents = replaySequence;
+		fixture.replayValid = replayValid;
+		
+		String typeOther = "typeString";
+		boolean replayValidOther = true;
+		String replayableReplayOther1 = "replayString1";
+		String replayableReplayOther2 = "replayString2";
+		String replaybleTargetOther1 = "replayTargetString1";
+		String replaybleTargetOther2 = "replayTargetString2";
+		MockReplayable replayableOther1 = new MockReplayable(replayableReplayOther1,
+				replaybleTargetOther1);
+		MockReplayable replayableOther2 = new MockReplayable(replayableReplayOther2, replaybleTargetOther2);
+		List<MockReplayable> replaySequenceOther = new LinkedList<MockReplayable>();
+		replaySequenceOther.add(replayableOther1);
+		replaySequenceOther.add(replayableOther2);
+		ReplayableEvent<MockReplayable> other = new ReplayableEvent<MockReplayable>(
+				typeOther);
+		other.replayEvents = replaySequenceOther;
+		other.replayValid = replayValidOther;
+
+		boolean result = fixture.equals(other);
+
+		assertEquals(true, result);
+	}
+	
+	@Test
+	public void testEquals_2() throws Exception {
+		String type = "typeString";
+		boolean replayValid = true;
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replayableTarget1 = "replayTargetString1";
+		String replayableTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replayableTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replayableTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayEvents = replaySequence;
+		fixture.replayValid = replayValid;
+		
+		String typeOther = "typeString2";
+		boolean replayValidOther = true;
+		String replayableReplayOther1 = "replayString1";
+		String replayableReplayOther2 = "replayString2";
+		String replaybleTargetOther1 = "replayTargetString1";
+		String replaybleTargetOther2 = "replayTargetString2";
+		MockReplayable replayableOther1 = new MockReplayable(replayableReplayOther1,
+				replaybleTargetOther1);
+		MockReplayable replayableOther2 = new MockReplayable(replayableReplayOther2, replaybleTargetOther2);
+		List<MockReplayable> replaySequenceOther = new LinkedList<MockReplayable>();
+		replaySequenceOther.add(replayableOther1);
+		replaySequenceOther.add(replayableOther2);
+		ReplayableEvent<MockReplayable> other = new ReplayableEvent<MockReplayable>(
+				typeOther);
+		other.replayEvents = replaySequenceOther;
+		other.replayValid = replayValidOther;
+
+		boolean result = fixture.equals(other);
+
+		assertEquals(false, result);
+	}
+	
+	@Test
+	public void testEquals_3() throws Exception {
+		String type = "typeString";
+		boolean replayValid = true;
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replayableTarget1 = "replayTargetString1";
+		String replayableTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replayableTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replayableTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayEvents = replaySequence;
+		fixture.replayValid = replayValid;
+		
+		String typeOther = "typeString";
+		boolean replayValidOther = true;
+		String replayableReplayOther1 = "replayString3";
+		String replayableReplayOther2 = "replayString2";
+		String replaybleTargetOther1 = "replayTargetString1";
+		String replaybleTargetOther2 = "replayTargetString2";
+		MockReplayable replayableOther1 = new MockReplayable(replayableReplayOther1,
+				replaybleTargetOther1);
+		MockReplayable replayableOther2 = new MockReplayable(replayableReplayOther2, replaybleTargetOther2);
+		List<MockReplayable> replaySequenceOther = new LinkedList<MockReplayable>();
+		replaySequenceOther.add(replayableOther1);
+		replaySequenceOther.add(replayableOther2);
+		ReplayableEvent<MockReplayable> other = new ReplayableEvent<MockReplayable>(
+				typeOther);
+		other.replayEvents = replaySequenceOther;
+		other.replayValid = replayValidOther;
+
+		boolean result = fixture.equals(other);
+
+		assertEquals(true, result);
+	}
+	
+	@Test
+	public void testEquals_4() throws Exception {
+		String type = "typeString";
+		boolean replayValid = true;
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replayableTarget1 = "replayTargetString1";
+		String replayableTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replayableTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replayableTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayEvents = replaySequence;
+		fixture.replayValid = replayValid;
+		
+		String typeOther = "typeString";
+		boolean replayValidOther = true;
+		String replayableReplayOther1 = "replayString1";
+		String replayableReplayOther2 = "replayString3";
+		String replaybleTargetOther1 = "replayTargetString1";
+		String replaybleTargetOther2 = "replayTargetString2";
+		MockReplayable replayableOther1 = new MockReplayable(replayableReplayOther1,
+				replaybleTargetOther1);
+		MockReplayable replayableOther2 = new MockReplayable(replayableReplayOther2, replaybleTargetOther2);
+		List<MockReplayable> replaySequenceOther = new LinkedList<MockReplayable>();
+		replaySequenceOther.add(replayableOther1);
+		replaySequenceOther.add(replayableOther2);
+		ReplayableEvent<MockReplayable> other = new ReplayableEvent<MockReplayable>(
+				typeOther);
+		other.replayEvents = replaySequenceOther;
+		other.replayValid = replayValidOther;
+
+		boolean result = fixture.equals(other);
+
+		assertEquals(true, result);
+	}
+	
+	@Test
+	public void testEquals_5() throws Exception {
+		String type = "typeString";
+		boolean replayValid = true;
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replayableTarget1 = "replayTargetString1";
+		String replayableTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replayableTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replayableTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayEvents = replaySequence;
+		fixture.replayValid = replayValid;
+		
+		String typeOther = "typeString";
+		boolean replayValidOther = false;
+		String replayableReplayOther1 = "replayString1";
+		String replayableReplayOther2 = "replayString2";
+		String replaybleTargetOther1 = "replayTargetString1";
+		String replaybleTargetOther2 = "replayTargetString2";
+		MockReplayable replayableOther1 = new MockReplayable(replayableReplayOther1,
+				replaybleTargetOther1);
+		MockReplayable replayableOther2 = new MockReplayable(replayableReplayOther2, replaybleTargetOther2);
+		List<MockReplayable> replaySequenceOther = new LinkedList<MockReplayable>();
+		replaySequenceOther.add(replayableOther1);
+		replaySequenceOther.add(replayableOther2);
+		ReplayableEvent<MockReplayable> other = new ReplayableEvent<MockReplayable>(
+				typeOther);
+		other.replayEvents = replaySequenceOther;
+		other.replayValid = replayValidOther;
+
+		boolean result = fixture.equals(other);
+
+		assertEquals(true, result);
+	}
+	
+	@Test
+	public void testEquals_6() throws Exception {
+		String type = "typeString";
+		boolean replayValid = true;
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replayableTarget1 = "replayTargetString1";
+		String replayableTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replayableTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replayableTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayEvents = replaySequence;
+		fixture.replayValid = replayValid;
+
+		boolean result = fixture.equals(fixture);
+
+		assertEquals(true, result);
+	}
+
+	@Test
+	public void testGetReplayDecorator_1() throws Exception {
+		String type = "typeString";
+		StubReplayDecorator decorator = new StubReplayDecorator();
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.decorator = decorator; 
+
+		IReplayDecorator result = fixture.getReplayDecorator();
+
+		assertEquals(decorator, result);
+	}
+
+	@Test
+	public void testGetReplayMessages_1() throws Exception {
+		String type = "typeString";
+		String replayableReplay1 = "replayString1";
+		String replayableReplay2 = "replayString2";
+		String replayableTarget1 = "replayTargetString1";
+		String replayableTarget2 = "replayTargetString2";
+		MockReplayable replayable1 = new MockReplayable(replayableReplay1,
+				replayableTarget1);
+		MockReplayable replayable2 = new MockReplayable(replayableReplay2, replayableTarget2);
+		List<MockReplayable> replaySequence = new LinkedList<MockReplayable>();
+		replaySequence.add(replayable1);
+		replaySequence.add(replayable2);
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayEvents = replaySequence;
+
+		List<MockReplayable> result = fixture.getReplayMessages();
+
+		ListAssert.assertEquals(replaySequence, result);
+	}
+
+	@Test
+	public void testHasValidReplay_1() throws Exception {
+		String type = "typeString";
+		boolean replayValid = true;
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayValid = replayValid;
+
+		boolean result = fixture.hasValidReplay();
+
+		assertEquals(replayValid, result);
+	}
+	
+	@Test
+	public void testHasValidReplay_2() throws Exception {
+		String type = "typeString";
+		boolean replayValid = false;
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		fixture.replayValid = replayValid;
+
+		boolean result = fixture.hasValidReplay();
+
+		assertEquals(replayValid, result);
+	}
+
+	@Test
+	public void testInvalidateReplay_1() throws Exception {
+		String type = "typeString";
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		
+		fixture.invalidateReplay();
+
+		assertFalse(fixture.replayValid);
+	}
+	
+	@Test
+	public void testInvalidateReplay_2() throws Exception {
+		String type = "typeString";
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+		
+		fixture.invalidateReplay();
+		fixture.invalidateReplay();
+
+		assertFalse(fixture.replayValid);
+	}
+
+	@Test
+	public void testSetDecorator_fixture_1() throws Exception {
+		String type = "typeString";
+		StubReplayDecorator decorator = new StubReplayDecorator();
+		ReplayableEvent<MockReplayable> fixture = new ReplayableEvent<MockReplayable>(
+				type);
+
+		fixture.setDecorator(decorator);
+
+		assertEquals(decorator, fixture.decorator);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(ReplayableEventTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/TestAll.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/TestAll.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/TestAll.java	(revision 432)
@@ -0,0 +1,25 @@
+package de.ugoe.cs.quest.data;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * The class <code>TestAll</code> builds a suite that can be used to run all
+ * of the tests within its package as well as within any subpackages of its
+ * package.
+ *
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+	EventTest.class,
+	ReplayableEventTest.class
+})
+public class TestAll {
+
+	public static void main(String[] args) {
+		JUnitCore.runClasses(new Class[] { TestAll.class });
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/mock/MockReplayable.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/mock/MockReplayable.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/data/mock/MockReplayable.java	(revision 432)
@@ -0,0 +1,61 @@
+package de.ugoe.cs.quest.data.mock;
+
+import de.ugoe.cs.quest.data.IReplayable;
+
+public class MockReplayable implements IReplayable {
+
+	private static final long serialVersionUID = 1L;
+
+	final String replay;
+	final String target;
+
+	public MockReplayable(String replay, String target) {
+		this.replay = replay;
+		this.target = target;
+	}
+
+	@Override
+	public String getReplay() {
+		return replay;
+	}
+
+	@Override
+	public String getTarget() {
+		return target;
+	}
+
+	@Override
+	public boolean equals(Object other) {
+		if (this == other) {
+			return true;
+		}
+		if (other instanceof MockReplayable) {
+			if (replay != null && target != null) {
+				return replay.equals(((MockReplayable) other).replay)
+						&& target.equals(((MockReplayable) other).target);
+			} else if (replay != null && target == null) {
+				return replay.equals(((MockReplayable) other).replay)
+						&& ((MockReplayable) other).target == null;
+			} else if (replay == null && target != null) {
+				return ((MockReplayable) other).replay == null
+						&& target.equals(((MockReplayable) other).target);
+			} else {
+				return ((MockReplayable) other).replay == null
+						&& ((MockReplayable) other).target == null;
+			}
+		}
+		return false;
+	}
+	
+	@Override
+	public int hashCode() {
+		int hashCode = 17;
+		if( replay!=null ) {
+			hashCode *= replay.hashCode();
+		}
+		if( target!=null ) {
+			hashCode *= target.hashCode();
+		}
+		return hashCode;
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/DeterministicFiniteAutomatonTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/DeterministicFiniteAutomatonTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/DeterministicFiniteAutomatonTest.java	(revision 432)
@@ -0,0 +1,157 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends 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.security.InvalidParameterException.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<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+		context.add(new Event<String>("b"));
+
+		Event<String> symbol = new Event<String>("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<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+
+		Event<String> symbol = new Event<String>("e");
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(0.0d, result, 0.0001);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testGetProbability_5() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+
+		Event<String> symbol = null;
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testGetProbability_6() throws Exception {
+		DeterministicFiniteAutomaton fixture = new DeterministicFiniteAutomaton(
+				new Random());
+		fixture.train(sequences);
+
+		List<Event<String>> context = null;
+
+		Event<String> symbol = new Event<String>("a");
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		List<Event<?>> sequence = new ArrayList<Event<?>>();
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("c"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("d"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+
+		sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore()
+				.run(DeterministicFiniteAutomatonTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/FirstOrderMarkovModelTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/FirstOrderMarkovModelTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/FirstOrderMarkovModelTest.java	(revision 432)
@@ -0,0 +1,94 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import org.junit.*;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.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<? extends 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.security.InvalidParameterException.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<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("c"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("d"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+
+		sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(FirstOrderMarkovModelTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/HighOrderMarkovModelTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/HighOrderMarkovModelTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/HighOrderMarkovModelTest.java	(revision 432)
@@ -0,0 +1,241 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends 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.security.InvalidParameterException.class)
+	public void testHighOrderMarkovModel_3() throws Exception {
+		int maxOrder = 1;
+		Random r = null;
+
+		new HighOrderMarkovModel(maxOrder, r);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>> context = new ArrayList<Event<String>>();
+		context.add(new Event<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("b"));
+
+		Event<String> symbol = new Event<String>("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<String>("b"));
+
+		Event<String> symbol = new Event<String>("a");
+
+		double result = fixture.getProbability(context, symbol);
+
+		assertEquals(5.0d / 13.0, result, 0.0001);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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<String>("b"));
+
+		Event<String> symbol = null;
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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<String> symbol = new Event<String>("b");
+
+		fixture.getProbability(context, symbol);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		List<Event<?>> sequence = new ArrayList<Event<?>>();
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("c"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("d"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+
+		sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(HighOrderMarkovModelTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/IncompleteMemoryTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/IncompleteMemoryTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/IncompleteMemoryTest.java	(revision 432)
@@ -0,0 +1,152 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.*;
+
+import de.ugoe.cs.quest.models.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.security.InvalidParameterException.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/quest-core-event-test/src/de/ugoe/cs/quest/models/MockTrieBasedModel.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/MockTrieBasedModel.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/MockTrieBasedModel.java	(revision 432)
@@ -0,0 +1,31 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends 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/quest-core-event-test/src/de/ugoe/cs/quest/models/ModelFlattenerTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/ModelFlattenerTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/ModelFlattenerTest.java	(revision 432)
@@ -0,0 +1,148 @@
+package de.ugoe.cs.quest.models;
+
+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.quest.data.Event;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.HighOrderMarkovModel;
+import de.ugoe.cs.quest.models.ModelFlattener;
+import de.ugoe.cs.quest.models.PredictionByPartialMatch;
+import de.ugoe.cs.quest.models.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<? extends Event<?>>> sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+		model.train(sequences);
+		
+		Collection<Event<?>> expectedSymbols = new HashSet<Event<?>>();
+		expectedSymbols.add(new Event<Object>("a-=-END"));
+		expectedSymbols.add(new Event<Object>("a-=-b"));
+		expectedSymbols.add(new Event<Object>("a-=-c"));
+		expectedSymbols.add(new Event<Object>("a-=-d"));
+		expectedSymbols.add(new Event<Object>("b-=-r"));
+		expectedSymbols.add(new Event<Object>("c-=-a"));
+		expectedSymbols.add(new Event<Object>("d-=-a"));
+		expectedSymbols.add(new Event<Object>("r-=-a"));
+		expectedSymbols.add(new Event<Object>("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<Object>("a-=-END"));
+		TrieNode<Event<?>> root_ab = root.getChild(new Event<Object>("a-=-b"));
+		TrieNode<Event<?>> root_ab_br = root_ab.getChild(new Event<Object>("b-=-r"));
+		TrieNode<Event<?>> root_ac = root.getChild(new Event<Object>("a-=-c"));
+		TrieNode<Event<?>> root_ac_ca = root_ac.getChild(new Event<Object>("c-=-a"));
+		TrieNode<Event<?>> root_ad = root.getChild(new Event<Object>("a-=-d"));
+		TrieNode<Event<?>> root_ad_da = root_ad.getChild(new Event<Object>("d-=-a"));
+		TrieNode<Event<?>> root_br = root.getChild(new Event<Object>("b-=-r"));
+		TrieNode<Event<?>> root_br_ra = root_br.getChild(new Event<Object>("r-=-a"));
+		TrieNode<Event<?>> root_ca = root.getChild(new Event<Object>("c-=-a"));
+		TrieNode<Event<?>> root_ca_ad = root_ca.getChild(new Event<Object>("a-=-d"));
+		TrieNode<Event<?>> root_da = root.getChild(new Event<Object>("d-=-a"));
+		TrieNode<Event<?>> root_da_ab = root_da.getChild(new Event<Object>("a-=-b"));
+		TrieNode<Event<?>> root_ra = root.getChild(new Event<Object>("r-=-a"));
+		TrieNode<Event<?>> root_ra_ac = root_ra.getChild(new Event<Object> ("a-=-c"));
+		TrieNode<Event<?>> root_ra_aEnd = root_ra.getChild(new Event<Object>("a-=-END"));
+		TrieNode<Event<?>> root_startA = root.getChild(new Event<Object>("START-=-a"));
+		TrieNode<Event<?>> root_startA_ab = root_startA.getChild(new Event<Object>("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<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("c"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("d"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(ModelFlattenerTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/PredictionByPartialMatchTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/PredictionByPartialMatchTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/PredictionByPartialMatchTest.java	(revision 432)
@@ -0,0 +1,365 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends 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.security.InvalidParameterException.class)
+	public void testPredictionByPartialMatch_2() throws Exception {
+		int markovOrder = -1;
+		Random r = new Random();
+
+		new PredictionByPartialMatch(markovOrder, r);
+	}
+	
+	@Test(expected = java.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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.security.InvalidParameterException.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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+
+		Event<String> symbol = new Event<String>("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<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("c"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("d"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+
+		sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+	}
+
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore()
+				.run(PredictionByPartialMatchTest.class);
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TestAll.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TestAll.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TestAll.java	(revision 432)
@@ -0,0 +1,31 @@
+package de.ugoe.cs.quest.models;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * The class <code>TestAll</code> builds a suite that can be used to run all
+ * of the tests within its package as well as within any subpackages of its
+ * package.
+ *
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+	DeterministicFiniteAutomatonTest.class,
+	FirstOrderMarkovModelTest.class,
+	HighOrderMarkovModelTest.class,
+	IncompleteMemoryTest.class,
+	ModelFlattenerTest.class,
+	PredictionByPartialMatchTest.class,
+	TrieBasedModelTest.class,	
+	TrieTest.class
+})
+public class TestAll {
+
+	public static void main(String[] args) {
+		JUnitCore.runClasses(new Class[] { TestAll.class });
+	}
+}
Index: /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TrieBasedModelTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TrieBasedModelTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TrieBasedModelTest.java	(revision 432)
@@ -0,0 +1,635 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.Trie;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.quest.models.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<String>("a"));
+		TrieNode<Event<?>> root_a_a = root_a.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_a_b = root_a.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_a_b_a = root_a_b
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_a_b_b = root_a_b
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_a_b_c = root_a_b
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_a_b_d = root_a_b
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_a_b_r = root_a_b
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_a_c = root_a.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_a_c_a = root_a_c
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_a_c_b = root_a_c
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_a_c_c = root_a_c
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_a_c_d = root_a_c
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_a_c_r = root_a_c
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_a_d = root_a.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_a_d_a = root_a_d
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_a_d_b = root_a_d
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_a_d_c = root_a_d
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_a_d_d = root_a_d
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_a_d_r = root_a_d
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_a_r = root_a.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_b = root.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_b_a = root_b.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_b_b = root_b.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_b_c = root_b.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_b_d = root_b.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_b_r = root_b.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_b_r_a = root_b_r
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_b_r_b = root_b_r
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_b_r_c = root_b_r
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_b_r_d = root_b_r
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_b_r_r = root_b_r
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_c = root.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_c_a = root_c.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_c_a_a = root_c_a
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_c_a_b = root_c_a
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_c_a_c = root_c_a
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_c_a_d = root_c_a
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_c_a_r = root_c_a
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_c_b = root_c.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_c_c = root_c.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_c_d = root_c.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_c_r = root_c.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_d = root.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_d_a = root_d.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_d_a_a = root_d_a
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_d_a_b = root_d_a
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_d_a_c = root_d_a
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_d_a_d = root_d_a
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_d_a_r = root_d_a
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_d_b = root_d.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_d_c = root_d.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_d_d = root_d.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_d_r = root_d.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_r = root.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_r_a = root_r.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_r_a_a = root_r_a
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_r_a_b = root_r_a
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_r_a_c = root_r_a
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_r_a_d = root_r_a
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_r_a_r = root_r_a
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_r_a_end = root_r_a.getChild(Event.ENDEVENT);
+		TrieNode<Event<?>> root_r_b = root_r.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_r_c = root_r.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_r_d = root_r.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_r_r = root_r.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_start = root.getChild(Event.STARTEVENT);
+		TrieNode<Event<?>> root_start_a = root_start
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_start_a_a = root_start_a
+				.getChild(new Event<String>("a"));
+		TrieNode<Event<?>> root_start_a_b = root_start_a
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_start_a_c = root_start_a
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_start_a_d = root_start_a
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_start_a_r = root_start_a
+				.getChild(new Event<String>("r"));
+		TrieNode<Event<?>> root_start_b = root_start
+				.getChild(new Event<String>("b"));
+		TrieNode<Event<?>> root_start_c = root_start
+				.getChild(new Event<String>("c"));
+		TrieNode<Event<?>> root_start_d = root_start
+				.getChild(new Event<String>("d"));
+		TrieNode<Event<?>> root_start_r = root_start
+				.getChild(new Event<String>("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.security.InvalidParameterException.class)
+	public void testTrieBasedModel_2() throws Exception {
+		int markovOrder = -1;
+		Random r = new Random();
+
+		new MockTrieBasedModel(markovOrder, r);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.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<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<String>("a"));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(new Event<String>("a"));
+		list.add(new Event<String>("b"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(new Event<String>("a"));
+		list.add(new Event<String>("c"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(new Event<String>("a"));
+		list.add(new Event<String>("d"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(new Event<String>("b"));
+		list.add(new Event<String>("r"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(new Event<String>("c"));
+		list.add(new Event<String>("a"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(new Event<String>("d"));
+		list.add(new Event<String>("a"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(new Event<String>("r"));
+		list.add(new Event<String>("a"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event<String>("a"));
+		expected.add(list);
+
+		Collection<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<String>("a"));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event<String>("a"));
+		list.add(new Event<String>("b"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event<String>("a"));
+		list.add(new Event<String>("c"));
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event<String>("a"));
+		list.add(new Event<String>("d"));
+		expected.add(list);
+
+		Collection<List<? extends Event<?>>> result = fixture
+				.generateSequences(length, true);
+
+		assertCollectionContent(expected, result);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testGenerateSequences_3() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<String>("a"));
+		list.add(new Event<String>("c"));
+		list.add(new Event<String>("a"));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+		list = new ArrayList<Event<?>>();
+		list.add(Event.STARTEVENT);
+		list.add(new Event<String>("a"));
+		list.add(new Event<String>("d"));
+		list.add(new Event<String>("a"));
+		list.add(Event.ENDEVENT);
+		expected.add(list);
+
+		Collection<List<? extends Event<?>>> result = fixture
+				.generateValidSequences(length);
+
+		assertCollectionContent(expected, result);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testGenerateValidSequences_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+
+		fixture.train(sequences);
+
+		Collection<? extends 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<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+		sequences.add(sequence);
+
+		fixture.train(sequences);
+
+		assertCollectionContent(symbols, fixture.getEvents());
+
+		assertTrieStructure(fixture.trie, 2);
+	}
+	
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testTrain_3() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<? extends 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<? extends Event<?>>> sequences = new ArrayList<List<? extends Event<?>>>();
+		sequences.add(sequence);
+		fixture.train(sequences);
+
+		fixture.update(sequences);
+
+		assertCollectionContent(symbols, fixture.getEvents());
+		assertTrieStructure(fixture.trie, 2);
+	}
+
+	@Test(expected = java.security.InvalidParameterException.class)
+	public void testUpdate_2() throws Exception {
+		int markovOrder = 2;
+		MockTrieBasedModel fixture = new MockTrieBasedModel(markovOrder,
+				new Random());
+		Collection<List<? extends Event<?>>> sequences = null;
+		fixture.trie = null;
+
+		fixture.update(sequences);
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		sequence = new ArrayList<Event<?>>();
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("c"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("d"));
+		sequence.add(new Event<String>("a"));
+		sequence.add(new Event<String>("b"));
+		sequence.add(new Event<String>("r"));
+		sequence.add(new Event<String>("a"));
+
+		symbols = new HashSet<Event<?>>();
+		symbols.add(new Event<String>("a"));
+		symbols.add(new Event<String>("b"));
+		symbols.add(new Event<String>("c"));
+		symbols.add(new Event<String>("d"));
+		symbols.add(new Event<String>("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/quest-core-event-test/src/de/ugoe/cs/quest/models/TrieTest.java
===================================================================
--- /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TrieTest.java	(revision 432)
+++ /trunk/quest-core-event-test/src/de/ugoe/cs/quest/models/TrieTest.java	(revision 432)
@@ -0,0 +1,727 @@
+package de.ugoe.cs.quest.models;
+
+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.quest.models.Trie;
+import de.ugoe.cs.quest.models.TrieNode;
+import de.ugoe.cs.quest.models.Trie.Edge;
+import de.ugoe.cs.quest.models.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.security.InvalidParameterException.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.security.InvalidParameterException.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/quest-core-events/src/de/ugoe/cs/quest/IReplayDecorator.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/IReplayDecorator.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/IReplayDecorator.java	(revision 432)
@@ -0,0 +1,55 @@
+package de.ugoe.cs.quest;
+
+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/quest-core-events/src/de/ugoe/cs/quest/SequenceInstanceOf.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/SequenceInstanceOf.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/SequenceInstanceOf.java	(revision 432)
@@ -0,0 +1,79 @@
+package de.ugoe.cs.quest;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import de.ugoe.cs.quest.data.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/quest-core-events/src/de/ugoe/cs/quest/assertions/AssertEvent.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/AssertEvent.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/AssertEvent.java	(revision 432)
@@ -0,0 +1,41 @@
+package de.ugoe.cs.quest.assertions;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.IReplayable;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+
+/**
+ * <p>
+ * Subclass of {@link ReplayableEvent} for assertions
+ * </p>
+ * 
+ * @author Jeffrey Hall
+ * @version 1.0
+ * 
+ * @param <T>
+ *            Allows only types that extend {@link IReplayable} and is used to
+ *            define the type of the assertion.
+ * 
+ */
+public class AssertEvent<T extends IReplayable> extends ReplayableEvent<T> {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new event with the given type.
+	 * <p>
+	 * 
+	 * @param type
+	 *            type of the event
+	 * @see Event#Event(String)
+	 */
+	public AssertEvent(String type) {
+		super(type);
+	}
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/FileEqualsReplay.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/FileEqualsReplay.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/FileEqualsReplay.java	(revision 432)
@@ -0,0 +1,91 @@
+package de.ugoe.cs.quest.assertions;
+
+import java.security.InvalidParameterException;
+
+import de.ugoe.cs.quest.data.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 InvalidParameterException
+	 *             thrown if expectedFile or actualFile are null
+	 */
+	public FileEqualsReplay(String expectedFile, String actualFile) {
+		if (expectedFile == null) {
+			throw new InvalidParameterException(
+					"expected file must not be null");
+		}
+		if (actualFile == null) {
+			throw new InvalidParameterException("actual file must not be null");
+		}
+		this.expectedFile = expectedFile;
+		this.actualFile = actualFile;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.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.quest.data.IReplayable#getTarget()
+	 */
+	@Override
+	public String getTarget() {
+		return "targetNotUsed";
+	}
+
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/TextEqualsReplay.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/TextEqualsReplay.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/assertions/TextEqualsReplay.java	(revision 432)
@@ -0,0 +1,94 @@
+package de.ugoe.cs.quest.assertions;
+
+import java.security.InvalidParameterException;
+
+import de.ugoe.cs.quest.data.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 InvalidParameterException
+	 *             thrown if target is null
+	 */
+	public TextEqualsReplay(String expectedValue, String target) {
+		if (target == null) {
+			throw new InvalidParameterException("target must not be null");
+		}
+		this.expectedValue = expectedValue;
+		this.target = target;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.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.quest.data.IReplayable#getTarget()
+	 */
+	@Override
+	public String getTarget() {
+		return target;
+	}
+
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/CoverageCalculatorObserved.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/CoverageCalculatorObserved.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/CoverageCalculatorObserved.java	(revision 432)
@@ -0,0 +1,272 @@
+package de.ugoe.cs.quest.coverage;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends Event<?>>> sequences;
+
+	/**
+	 * <p>
+	 * Observed sequences that are baseline for the coverage calculation.
+	 * </p>
+	 */
+	private final Collection<List<? extends 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<? extends Event<?>>> subSeqsGenerated = null;
+
+	/**
+	 * <p>
+	 * All subsequences of {@link #length} of {@link #observedSequences}.
+	 * </p>
+	 */
+	private Collection<List<? extends 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 InvalidParameterException
+	 *             thrown if observedSequences or sequences is null or length
+	 *             less than or equal to 0
+	 */
+	public CoverageCalculatorObserved(
+			Collection<List<? extends Event<?>>> observedSequences,
+			Collection<List<? extends Event<?>>> sequences, int length) {
+		if (observedSequences == null) {
+			throw new InvalidParameterException(
+					"observed sequences must not be null");
+		}
+		if (sequences == null) {
+			throw new InvalidParameterException("sequences must not be null");
+		}
+		if (length <= 0) {
+			throw new InvalidParameterException(
+					"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<? extends Event<?>>> subSeqsObservedCopy = new LinkedHashSet<List<? extends 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<? extends Event<?>>, Double> weightMap = SequenceTools
+				.generateWeights(process, subSeqsObserved);
+
+		Collection<List<? extends Event<?>>> subSeqsObservedCopy = new LinkedHashSet<List<? extends Event<?>>>(
+				subSeqsObserved);
+		subSeqsObservedCopy.retainAll(subSeqsGenerated);
+		double weight = 0.0d;
+		for (List<? extends 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<? extends Event<?>>> subSeqsGeneratedCopy = new LinkedHashSet<List<? extends 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 InvalidParameterException
+	 *             thrown if process is null
+	 */
+	public double getCoveragePossibleNew(IStochasticProcess process) {
+		if (process == null) {
+			throw new InvalidParameterException("process must not be null");
+		}
+		createSubSeqs();
+		Collection<List<? extends Event<?>>> subSeqsGeneratedCopy = new LinkedHashSet<List<? extends Event<?>>>(
+				subSeqsGenerated);
+		Collection<List<? extends 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 InvalidParameterException
+	 *             thrown if process is null
+	 */
+	public double getCoveragePossibleNewWeight(IStochasticProcess process) {
+		if (process == null) {
+			throw new InvalidParameterException("process must not be null");
+		}
+		createSubSeqs();
+		Collection<List<? extends Event<?>>> subSeqsGeneratedCopy = new LinkedHashSet<List<? extends Event<?>>>(
+				subSeqsGenerated);
+		Collection<List<? extends Event<?>>> subSeqsPossible = process
+				.generateSequences(length);
+		subSeqsGeneratedCopy.removeAll(subSeqsObserved);
+		subSeqsPossible.removeAll(subSeqsObserved);
+		Map<List<? extends Event<?>>, Double> weightMap = SequenceTools
+				.generateWeights(process, subSeqsPossible);
+		double weight = 0.0d;
+		for (List<? extends 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<? extends Event<?>>> subSeqsGeneratedCopy = new LinkedHashSet<List<? extends 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/quest-core-events/src/de/ugoe/cs/quest/coverage/CoverageCalculatorProcess.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/CoverageCalculatorProcess.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/CoverageCalculatorProcess.java	(revision 432)
@@ -0,0 +1,218 @@
+package de.ugoe.cs.quest.coverage;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends 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<? extends Event<?>>> containedSubSeqs = null;
+
+	/**
+	 * <p>
+	 * All subsequences of {@link #length} that can be generated by
+	 * {@link #process}.
+	 * </p>
+	 */
+	private Collection<List<? extends Event<?>>> allPossibleSubSeqs = null;
+
+	/**
+	 * <p>
+	 * The probabilities of all subsequences of {@link #length} according to
+	 * {@link #process}.
+	 * </p>
+	 */
+	private Map<List<? extends 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 InvalidParameterException
+	 *             thrown if process or sequences is null or length less than or equal to 0
+	 */
+	public CoverageCalculatorProcess(IStochasticProcess process,
+			Collection<List<? extends Event<?>>> sequences, int length) {
+		if (process == null) {
+			throw new InvalidParameterException("process must not be null");
+		}
+		if (sequences == null) {
+			throw new InvalidParameterException("sequences must not be null");
+		}
+		if (length <= 0) {
+			throw new InvalidParameterException(
+					"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<? extends 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 InvalidParameterException
+	 *             thrown is newSequences is null
+	 */
+	public void setSequences(Collection<List<? extends Event<?>>> newSequences) {
+		if (newSequences == null) {
+			throw new InvalidParameterException("sequences must not be null");
+		}
+		this.sequences = newSequences;
+		containedSubSeqs = null;
+	}
+
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/SequenceTools.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/SequenceTools.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/coverage/SequenceTools.java	(revision 432)
@@ -0,0 +1,147 @@
+package de.ugoe.cs.quest.coverage;
+
+import java.security.InvalidParameterException;
+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.quest.data.Event;
+import de.ugoe.cs.quest.models.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<? extends Event<?>>, Double> generateWeights(
+			IStochasticProcess process,
+			Collection<List<? extends Event<?>>> sequences) {
+		Map<List<? extends Event<?>>, Double> subSeqWeights = new LinkedHashMap<List<? extends Event<?>>, Double>();
+		if (sequences != null && !sequences.isEmpty()) {
+			if (process != null) {
+				double sum = 0.0;
+				for (List<? extends Event<?>> sequence : sequences) {
+					double prob = process.getProbability(sequence);
+					subSeqWeights.put(sequence, prob);
+					sum += prob;
+				}
+				if (sum < 1.0) {
+					for (Map.Entry<List<? extends Event<?>>, Double> entry : subSeqWeights
+							.entrySet()) {
+						entry.setValue(entry.getValue() / sum);
+					}
+				}
+			} else {
+				for( List<? extends 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 InvalidParameterException
+	 *             thrown if length less or equal to 0
+	 */
+	public static long numSequences(IStochasticProcess process, int length) {
+		if (length <= 0) {
+			throw new InvalidParameterException(
+					"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 InvalidParameterException
+	 *             thrown if length less or equal to 0
+	 */
+	public static Set<List<? extends Event<?>>> containedSubSequences(
+			Collection<List<? extends Event<?>>> sequences, int length) {
+		if (length <= 0) {
+			throw new InvalidParameterException(
+					"length must be a positive integer");
+		}
+		Set<List<? extends Event<?>>> containedSubSeqs = new LinkedHashSet<List<? extends Event<?>>>();
+		if (sequences != null) {
+			for (List<? extends 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/quest-core-events/src/de/ugoe/cs/quest/data/Event.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/data/Event.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/data/Event.java	(revision 432)
@@ -0,0 +1,327 @@
+package de.ugoe.cs.quest.data;
+
+import java.io.Serializable;
+import java.security.InvalidParameterException;
+
+/**
+ * <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<T> implements Serializable {
+
+	/**
+	 * Id for object serialization.
+	 */
+	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<Object> STARTEVENT = new Event<Object>("START");
+
+	/**
+	 * <p>
+	 * Global end event that can be used to indicate the end of a sequence.
+	 */
+	public static final Event<Object> ENDEVENT = new Event<Object>("END");
+
+	/**
+	 * <p>
+	 * Type of the event.
+	 * </p>
+	 */
+	protected String type;
+
+	/**
+	 * </p> Target of the event.
+	 */
+	protected String target = null;
+
+	/**
+	 * <p>
+	 * Short description of the event target.
+	 * </p>
+	 */
+	protected String targetShort = null;
+
+	/**
+	 * Further information about the event that shall be included in its Id.
+	 */
+	protected String idInfo = "";
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new Event with a given type.
+	 * </p>
+	 * 
+	 * @param type
+	 *            type of the event
+	 */
+	public Event(String type) {
+		if (type == null) {
+			throw new InvalidParameterException("Event type must not be null");
+		}
+		this.type = type;
+	}
+
+	/**
+	 * <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 (otherEvent.canEqual(this)) {
+				if (type != null) {
+					return targetEquals(otherEvent.target)
+							&& type.equals(otherEvent.type);
+				} else {
+					return targetEquals(otherEvent.target)
+							&& otherEvent.type == null;
+				}
+			} else {
+				return false;
+			}
+		} else {
+			return false;
+		}
+	}
+
+	public boolean canEqual(Object other) {
+		return (other instanceof Event<?>);
+	}
+
+	/**
+	 * <p>
+	 * Returns {@link #getStandardId()} as String representation of the event.
+	 * </p>
+	 * 
+	 * @return String represenation of the event
+	 */
+	@Override
+	public String toString() {
+		return getStandardId();
+	}
+
+	/**
+	 * Informations about the event important for its Id that is neither target
+	 * nor type.
+	 * 
+	 * @return {@link #idInfo} of the event
+	 */
+	public String getIdInfo() {
+		return idInfo;
+	}
+
+	/**
+	 * <p>
+	 * If {@link #targetShort} is set, a shortend version of the Id is returned
+	 * of the form {@link #targetShort}.{@link #type}.{@link #idInfo} is
+	 * returned. Otherwise the standard Id is returned (see
+	 * {@link #getStandardId()}).
+	 * </p>
+	 * 
+	 * @return if available, shortend Id string; {@link #getStandardId()}
+	 *         otherwise
+	 */
+	public String getShortId() {
+		String shortId = null;
+		if (targetShort != null) {
+			shortId = targetShort + "." + getType();
+			if (!"".equals(idInfo)) {
+				shortId += "." + idInfo;
+			}
+		} else {
+			shortId = getStandardId();
+		}
+		return shortId;
+	}
+
+	/**
+	 * <p>
+	 * Returns the Id string of the event. It has the form {@link #target}.
+	 * {@link #type}.{@link #idInfo};
+	 * <p>
+	 * 
+	 * @return Id string of the event
+	 */
+	public String getStandardId() {
+		String id = "";
+		if (target != null) {
+			id += target + ".";
+		}
+		id += getType();
+		if (!"".equals(idInfo)) {
+			id += "." + idInfo;
+		}
+		return id;
+	}
+
+	/**
+	 * <p>
+	 * Returns the {@link #target} of the event.
+	 * </p>
+	 * 
+	 * @return {@link #target} of the event
+	 */
+	public String getTarget() {
+		return target;
+	}
+
+	/**
+	 * <p>
+	 * Returns the {@link #targetShort} of the event.
+	 * </p>
+	 * 
+	 * @return {@link #targetShort} of the event
+	 */
+	protected String getTargetShort() {
+		return targetShort;
+	}
+
+	/**
+	 * <p>
+	 * Returns the {@link #type} of the event.
+	 * </p>
+	 * 
+	 * @return {@link #type} of the event
+	 */
+	public String getType() {
+		return type;
+	}
+
+	/*
+	 * (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();
+		}
+		hash = multiplier * hash + targetHashCode();
+
+		return hash;
+	}
+
+	/**
+	 * <p>
+	 * Sets the {@link #idInfo} of the event. The idInfo is optional and
+	 * contains information important for the event's Id that is neither target
+	 * nor type.
+	 * </p>
+	 * 
+	 * @param info
+	 *            {@link #idInfo} of the event
+	 */
+	public void setIdInfo(String info) {
+		idInfo = info;
+	}
+
+	/**
+	 * <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(String target) {
+		if (this.target != null) {
+			return false;
+		}
+		this.target = target;
+		return true;
+	}
+
+	/**
+	 * <p>
+	 * Sets the short description of the event target. Once set, the target
+	 * cannot be changed.
+	 * </p>
+	 * 
+	 * @param targetShort
+	 *            short target description
+	 * @return true, if target was changed, false otherwise
+	 */
+	public boolean setTargetShort(String targetShort) {
+		if (this.targetShort != null) {
+			return false;
+		}
+		this.targetShort = targetShort;
+		return true;
+	}
+
+	/**
+	 * <p>
+	 * This function is used by {@link #equals(Object)} to determine if the
+	 * targets of both events are equal. The standard implementation provided by
+	 * this class performs a String comparison between the target strings.
+	 * </p>
+	 * <p>
+	 * Subclasses can override this method to implemented more sophisticated
+	 * means for the target comparison, e.g., to account for changes in the
+	 * title of a widget.
+	 * </p>
+	 * 
+	 * @param otherTarget
+	 *            other target string to which the target if this event is
+	 *            compared to
+	 * @return true if the targets are equals; false otherwise
+	 */
+	protected boolean targetEquals(String otherTarget) {
+		boolean retVal;
+		if (target != null) {
+			retVal = target.equals(otherTarget);
+		} else {
+			retVal = (otherTarget == null);
+		}
+		return retVal;
+	}
+
+	/**
+	 * <p>
+	 * This function is used by {@link #hashCode()} to determine how the hash of
+	 * the {@link #target}. It has to be overridden by subclasses that implement
+	 * {@link #targetEquals(String)}, to ensure that the equals/hashCode
+	 * contract remains valid.
+	 * </p>
+	 * 
+	 * @return hash of the target
+	 */
+	protected int targetHashCode() {
+		if (target != null) {
+			return target.hashCode();
+		} else {
+			return 0;
+		}
+	}
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/data/IReplayable.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/data/IReplayable.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/data/IReplayable.java	(revision 432)
@@ -0,0 +1,35 @@
+package de.ugoe.cs.quest.data;
+
+import java.io.Serializable;
+
+/**
+ * <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 target of the replayable.
+	 * </p>
+	 * 
+	 * @return target of the replayable
+	 */
+	String getTarget();
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/data/ReplayableEvent.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/data/ReplayableEvent.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/data/ReplayableEvent.java	(revision 432)
@@ -0,0 +1,167 @@
+package de.ugoe.cs.quest.data;
+
+import java.security.InvalidParameterException;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.quest.IReplayDecorator;
+
+/**
+ * <p>
+ * Subclass of {@link Event} for events that contain all informations required
+ * for replaying them, i.e., generating scripts that can used for automated
+ * software execution.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ * @param <T>
+ *            Allows only types that extend {@link IReplayable} and is used to
+ *            define a list of replayables that describe the replay of the
+ *            event.
+ */
+public class ReplayableEvent<T extends IReplayable> extends Event<T> {
+
+	/**
+	 * Id for object serialization.
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <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<T> replayEvents = new LinkedList<T>();;
+
+	/**
+	 * <p>
+	 * Defines whether the replay is valid or invalid. It may be invalid, e.g.,
+	 * due to errors during the generation of the event or lack of vital
+	 * information.
+	 * </p>
+	 */
+	protected boolean replayValid = true;
+
+	/**
+	 * <p>
+	 * {@link IReplayDecorator} used when replays of this type are written.
+	 * </p>
+	 */
+	protected IReplayDecorator decorator = null;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new event with the given type.
+	 * <p>
+	 * 
+	 * @param type
+	 *            type of the event
+	 * @see Event#Event(String)
+	 */
+	public ReplayableEvent(String type) {
+		super(type);
+	}
+
+	/**
+	 * <p>
+	 * Adds a new {@link IReplayable} of type T to the replay sequence.
+	 * </p>
+	 * 
+	 * @param replayable
+	 *            element that is added to the sequence
+	 * @throws InvalidParameterException
+	 *             thrown is replayable is null
+	 */
+	public void addReplayEvent(T replayable) {
+		if (replayable == null) {
+			throw new InvalidParameterException("replayble must not be null");
+		}
+		replayEvents.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 InvalidParameterException
+	 *             thrown if generatedReplaySeq is null
+	 */
+	public void addReplaySequence(List<T> generatedReplaySeq) {
+		if (generatedReplaySeq == null) {
+			throw new InvalidParameterException(
+					"generatedReplaySeq must not be null");
+		}
+		replayEvents.addAll(generatedReplaySeq);
+	}
+
+	/**
+	 * <p>
+	 * Returns the {@link IReplayDecorator} of the event.
+	 * </p>
+	 * 
+	 * @return {@link IReplayDecorator} of the event; null if no decorator has
+	 *         been set
+	 */
+	public IReplayDecorator getReplayDecorator() {
+		return decorator;
+	}
+
+	/**
+	 * <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<T> getReplayMessages() {
+		return new LinkedList<T>(replayEvents);
+	}
+
+	/**
+	 * <p>
+	 * Returns whether the replay is valid or not.
+	 * </p>
+	 * 
+	 * @return true, if replay is valid; false otherwise.
+	 */
+	public boolean hasValidReplay() {
+		return replayValid;
+	}
+
+	/**
+	 * <p>
+	 * Marks the replay as invalid. Once marked as invalid, it remains so and
+	 * cannot be changed back to valid.
+	 * </p>
+	 */
+	public void invalidateReplay() {
+		replayValid = false;
+	}
+
+	/**
+	 * <p>
+	 * Sets the {@link IReplayDecorator} associated with the event.
+	 * </p>
+	 * 
+	 * @param decorator
+	 *            decorator associated with the event
+	 */
+	public void setDecorator(IReplayDecorator decorator) {
+		this.decorator = decorator;
+	}
+	
+	@Override
+	public boolean canEqual(Object other) {
+		return (other instanceof ReplayableEvent<?>);
+	}
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/models/DeterministicFiniteAutomaton.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/DeterministicFiniteAutomaton.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/DeterministicFiniteAutomaton.java	(revision 432)
@@ -0,0 +1,80 @@
+package de.ugoe.cs.quest.models;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.data.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.quest.models.IStochasticProcess#getProbability(java.util.List,
+	 *      de.ugoe.cs.quest.data.Event)
+	 */
+	@Override
+	public double getProbability(List<? extends Event<?>> context,
+			Event<?> symbol) {
+		if( context==null ) {
+			throw new InvalidParameterException("context must not be null");
+		}
+		if( symbol==null ) {
+			throw new InvalidParameterException("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/quest-core-events/src/de/ugoe/cs/quest/models/FirstOrderMarkovModel.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/FirstOrderMarkovModel.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/FirstOrderMarkovModel.java	(revision 432)
@@ -0,0 +1,264 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.data.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).getStandardId();
+			if( id.equals(Event.STARTEVENT.getStandardId()) || id.contains(Event.STARTEVENT.getStandardId()+"-=-") ) {
+				startIndexList.add(i);
+			}
+			if( id.equals(Event.ENDEVENT.getStandardId()) || id.contains("-=-"+Event.ENDEVENT.getStandardId()) ) {
+				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("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.quest.models.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.getShortId().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.getShortId();
+			List<Event<?>> context = new ArrayList<Event<?>>();
+			context.add(symbol);
+
+			Collection<Event<?>> followers = trie.getFollowingSymbols(context);
+
+			for (Event<?> follower : followers) {
+				String to = follower.getShortId();
+				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/quest-core-events/src/de/ugoe/cs/quest/models/HighOrderMarkovModel.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/HighOrderMarkovModel.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/HighOrderMarkovModel.java	(revision 432)
@@ -0,0 +1,87 @@
+package de.ugoe.cs.quest.models;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.data.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.quest.models.IStochasticProcess#getProbability(java.util.List,
+	 *      de.ugoe.cs.quest.data.Event)
+	 */
+	@Override
+	public double getProbability(List<? extends Event<?>> context,
+			Event<?> symbol) {
+		if (context == null) {
+			throw new InvalidParameterException("context must not be null");
+		}
+		if (symbol == null) {
+			throw new InvalidParameterException("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/quest-core-events/src/de/ugoe/cs/quest/models/IDotCompatible.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IDotCompatible.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IDotCompatible.java	(revision 432)
@@ -0,0 +1,22 @@
+package de.ugoe.cs.quest.models;
+
+/**
+ * <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/quest-core-events/src/de/ugoe/cs/quest/models/IMemory.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IMemory.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IMemory.java	(revision 432)
@@ -0,0 +1,40 @@
+package de.ugoe.cs.quest.models;
+
+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/quest-core-events/src/de/ugoe/cs/quest/models/IStochasticProcess.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IStochasticProcess.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IStochasticProcess.java	(revision 432)
@@ -0,0 +1,194 @@
+package de.ugoe.cs.quest.models;
+
+import java.io.Serializable;
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.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 InvalidParameterException
+	 *             thrown if context or symbol is null
+	 */
+	double getProbability(List<? extends 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 InvalidParameterException
+	 *             thrown if sequence is null
+	 */
+	double getProbability(List<? extends 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<? extends 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<? extends 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 InvalidParameterException
+	 *             thrown if length is less than or equal to 0
+	 */
+	public Collection<List<? extends 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 InvalidParameterException
+	 *             thrown if length is less than or equal to 0
+	 */
+	public Collection<List<? extends 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 InvalidParameterException
+	 *             thrown if length is less than or equal to 0
+	 */
+	public Collection<List<? extends 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<? extends Event<?>> getEvents();
+
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IncompleteMemory.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IncompleteMemory.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/IncompleteMemory.java	(revision 432)
@@ -0,0 +1,95 @@
+package de.ugoe.cs.quest.models;
+
+import java.security.InvalidParameterException;
+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 InvalidParameterException
+	 *             This exception is thrown if the length is smaller than 1
+	 */
+	public IncompleteMemory(int length) {
+		if (length < 1) {
+			throw new InvalidParameterException(
+					"Length of IncompleteMemory must be at least 1.");
+		}
+		this.length = length;
+		history = new LinkedList<T>();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.models.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.quest.models.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/quest-core-events/src/de/ugoe/cs/quest/models/ModelFlattener.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/ModelFlattener.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/ModelFlattener.java	(revision 432)
@@ -0,0 +1,137 @@
+package de.ugoe.cs.quest.models;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+
+/**
+ * <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().getStandardId();
+			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<Object>(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()
+							.getStandardId());
+					TrieNode<Event<?>> firstOrderTransitionChild = firstOrderNode
+							.getChildCreate(new Event<Object>(transitionID
+									.toString()));
+					firstOrderTransitionChild.setCount(transitionChild
+							.getCount());
+				}
+			}
+		}
+	}
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/models/PredictionByPartialMatch.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/PredictionByPartialMatch.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/PredictionByPartialMatch.java	(revision 432)
@@ -0,0 +1,200 @@
+package de.ugoe.cs.quest.models;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.data.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 InvalidParameterException 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 InvalidParameterException("minOrder must be greather than or equal to 0");
+		}
+		if( minOrder>markovOrder) {
+			throw new InvalidParameterException("minOrder must be less than or equal to markovOrder");
+		}
+		if( probEscape<=0.0 || probEscape>=1.0) {
+			throw new InvalidParameterException("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.quest.models.IStochasticProcess#getProbability(java.util.List,
+	 *      de.ugoe.cs.quest.data.Event)
+	 */
+	@Override
+	public double getProbability(List<? extends Event<?>> context,
+			Event<?> symbol) {
+		if (context == null) {
+			throw new InvalidParameterException("context must not be null");
+		}
+		if (symbol == null) {
+			throw new InvalidParameterException("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/quest-core-events/src/de/ugoe/cs/quest/models/Trie.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/Trie.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/Trie.java	(revision 432)
@@ -0,0 +1,472 @@
+package de.ugoe.cs.quest.models;
+
+import java.io.Serializable;
+import java.security.InvalidParameterException;
+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 InvalidParameterException("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.quest.models.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/quest-core-events/src/de/ugoe/cs/quest/models/TrieBasedModel.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/TrieBasedModel.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/TrieBasedModel.java	(revision 432)
@@ -0,0 +1,416 @@
+package de.ugoe.cs.quest.models;
+
+import java.security.InvalidParameterException;
+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.quest.data.Event;
+import de.ugoe.cs.quest.models.Trie.Edge;
+import de.ugoe.cs.quest.models.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 InvalidParameterException
+	 *             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 InvalidParameterException(
+					"markov order must not be less than 0");
+		}
+		if (r == null) {
+			throw new InvalidParameterException(
+					"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 InvalidParameterException
+	 *             thrown is sequences is null
+	 */
+	public void train(Collection<List<? extends 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 InvalidParameterException
+	 *             thrown is sequences is null
+	 */
+	public void update(Collection<List<? extends Event<?>>> sequences) {
+		if (sequences == null) {
+			throw new InvalidParameterException("sequences must not be null");
+		}
+		if (trie == null) {
+			trie = new Trie<Event<?>>();
+		}
+		for (List<? extends 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.quest.models.IStochasticProcess#randomSequence()
+	 */
+	@Override
+	public List<? extends Event<?>> randomSequence() {
+		return randomSequence(Integer.MAX_VALUE, true);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.models.IStochasticProcess#randomSequence()
+	 */
+	@Override
+	public List<? extends 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.quest.models.IStochasticProcess#getNumStates()
+	 */
+	@Override
+	public int getNumSymbols() {
+		if (trie == null) {
+			return 0;
+		} else {
+			return trie.getNumSymbols();
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.models.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.quest.models.IStochasticProcess#getEvents()
+	 */
+	@Override
+	public Collection<? extends Event<?>> getEvents() {
+		if (trie == null) {
+			return new HashSet<Event<?>>();
+		} else {
+			return trie.getKnownSymbols();
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * de.ugoe.cs.quest.models.IStochasticProcess#generateSequences(int)
+	 */
+	@Override
+	public Collection<List<? extends Event<?>>> generateSequences(int length) {
+		return generateSequences(length, false);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * de.ugoe.cs.quest.models.IStochasticProcess#generateSequences(int,
+	 * boolean)
+	 */
+	@Override
+	public Set<List<? extends Event<?>>> generateSequences(int length,
+			boolean fromStart) {
+		Set<List<? extends Event<?>>> sequenceSet = new LinkedHashSet<List<? extends Event<?>>>();
+		if (length < 1) {
+			throw new InvalidParameterException(
+					"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<? extends Event<?>> events = getEvents();
+		Collection<List<? extends Event<?>>> seqsShorter = generateSequences(
+				length - 1, fromStart);
+		for (Event<?> event : events) {
+			for (List<? extends 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.quest.models.IStochasticProcess#generateValidSequences
+	 * (int)
+	 */
+	@Override
+	public Collection<List<? extends Event<?>>> generateValidSequences(
+			int length) {
+		// check for min-length implicitly done by generateSequences
+		Collection<List<? extends Event<?>>> allSequences = generateSequences(
+				length, true);
+		Collection<List<? extends Event<?>>> validSequences = new LinkedHashSet<List<? extends Event<?>>>();
+		for (List<? extends 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.quest.models.IStochasticProcess#getProbability(java.util
+	 * .List)
+	 */
+	@Override
+	public double getProbability(List<? extends Event<?>> sequence) {
+		if (sequence == null) {
+			throw new InvalidParameterException("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.quest.models.IStochasticProcess#getNumFOMStates()
+	 */
+	@Override
+	public int getNumFOMStates() {
+		if (trie == null) {
+			return 0;
+		} else {
+			return trie.getNumLeafAncestors();
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.models.IStochasticProcess#getNumTransitions()
+	 */
+	@Override
+	public int getNumTransitions() {
+		if (trie == null) {
+			return 0;
+		} else {
+			return trie.getNumLeafs();
+		}
+	}
+}
Index: /trunk/quest-core-events/src/de/ugoe/cs/quest/models/TrieNode.java
===================================================================
--- /trunk/quest-core-events/src/de/ugoe/cs/quest/models/TrieNode.java	(revision 432)
+++ /trunk/quest-core-events/src/de/ugoe/cs/quest/models/TrieNode.java	(revision 432)
@@ -0,0 +1,443 @@
+package de.ugoe.cs.quest.models;
+
+import java.io.Serializable;
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.quest.models.Trie.Edge;
+import de.ugoe.cs.quest.models.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 InvalidParameterException(
+					"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 InvalidParameterException("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/quest-ui-core-test/src/de/ugoe/cs/quest/TestAll.java
===================================================================
--- /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/TestAll.java	(revision 432)
+++ /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/TestAll.java	(revision 432)
@@ -0,0 +1,22 @@
+package de.ugoe.cs.quest;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * The class <code>TestAll</code> builds a suite that can be used to run all
+ * of the tests within its package as well as within any subpackages of its
+ * package.
+ *
+ * @author Steffen Herbold
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+	de.ugoe.cs.quest.jfc.data.TestAll.class
+})
+public class TestAll {
+	public static void main(String[] args) {
+		JUnitCore.runClasses(new Class[] { TestAll.class });
+	}
+}
Index: /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/jfc/data/JFCEventTest.java
===================================================================
--- /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/jfc/data/JFCEventTest.java	(revision 432)
+++ /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/jfc/data/JFCEventTest.java	(revision 432)
@@ -0,0 +1,376 @@
+package de.ugoe.cs.quest.jfc.data;
+
+import org.junit.*;
+
+import de.ugoe.cs.quest.jfc.data.JFCEvent;
+import de.ugoe.cs.quest.jfc.data.JFCTargetComparator;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>JFCEventTest</code> contains tests for the class <code>{@link JFCEvent}</code>.
+ *
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class JFCEventTest {
+	
+	@Test
+	public void testTargetEquals_1()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_2()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['titleOther','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_3()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hashOther']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_4()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['titleOther','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_5()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hashOther'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_6()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['titleOther','class2','text2','index2','hashOther']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_7()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['titleOther','class1','text1','index1','hashOther'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_8()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['title2','classOther','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_9()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['title2','class2','textOther','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_10()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','indexOther','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_11()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','classOther','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_12()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','textOther','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_13()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title1','class1','text1','indexOther','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_14()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title'1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title'1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_15()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title]1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title]1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_16()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title[1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title[1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_17()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title].1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title].1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_18()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title.[1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		String target2 = "['title.[1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_19()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1']";
+		String target2 = "['title1','class1','text1','index1','hash1'].['title2','class2','text2','index2','hash2']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_20()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1']";
+		String target2 = "['title1','class1','text1','index1','hash1']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_21()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1']";
+		String target2 = "['titleOther','class1','text1','index1','hash1']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_22()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1']";
+		String target2 = "['title1','class1','text1','index1','hashOther']";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_23()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1']";
+		String target2 = "['titleOther','class1','text1','index1','hashOther']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_24()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = null;
+		String target2 = "['titleOther','class1','text1','index1','hash1']";
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_25()
+		throws Exception {
+		JFCEvent event = new JFCEvent("type");
+		String target1 = "['title1','class1','text1','index1','hash1']";
+		String target2 = null;
+		event.setTarget(target1);
+		boolean expected = false;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Before
+	public void setUp() {
+		JFCTargetComparator.reset();
+	}
+	
+	public static void main(String[] args) {
+		new org.junit.runner.JUnitCore().run(JFCEventTest.class);
+	}
+}
Index: /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/jfc/data/TestAll.java
===================================================================
--- /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/jfc/data/TestAll.java	(revision 432)
+++ /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/jfc/data/TestAll.java	(revision 432)
@@ -0,0 +1,25 @@
+package de.ugoe.cs.quest.jfc.data;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import de.ugoe.cs.quest.jfc.data.TestAll;
+
+/**
+ * The class <code>TestAll</code> builds a suite that can be used to run all
+ * of the tests within its package as well as within any subpackages of its
+ * package.
+ *
+ * @author Steffen Herbold
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+	JFCEventTest.class
+})
+public class TestAll {
+
+	public static void main(String[] args) {
+		JUnitCore.runClasses(new Class[] { TestAll.class });
+	}
+}
Index: /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/windows/data/WindowsEventTest.java
===================================================================
--- /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/windows/data/WindowsEventTest.java	(revision 432)
+++ /trunk/quest-ui-core-test/src/de/ugoe/cs/quest/windows/data/WindowsEventTest.java	(revision 432)
@@ -0,0 +1,50 @@
+package de.ugoe.cs.quest.windows.data;
+
+import org.junit.*;
+
+import de.ugoe.cs.quest.windows.data.MFCTargetComparator;
+import de.ugoe.cs.quest.windows.data.WindowsEvent;
+
+import static org.junit.Assert.*;
+
+/**
+ * The class <code>WindowsEventTest</code> contains tests for the class <code>{@link WindowsEvent}</code>.
+ *
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WindowsEventTest {
+	
+	@Test
+	public void testTargetEquals_1() throws Exception {
+		WindowsEvent event = new WindowsEvent("type");
+		String target1 = "<window name=\"name1\" class=\"class1\" resourceId=\"id1\" isModal=\"modal1\" hwnd=\"111\"/>";
+		String target2 = "<window name=\"name1\" class=\"class1\" resourceId=\"id1\" isModal=\"modal1\" hwnd=\"111\"/>";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+	
+	@Test
+	public void testTargetEquals_2() throws Exception {
+		WindowsEvent event = new WindowsEvent("type");
+		String target1 = "<window name=\"Messplatz\" class=\"#32770\" resourceId=\"0\" isModal=\"true\" hwnd=\"1770138\"/><window name=\"Messplatz im Einstellmodus\" class=\"#32770\" resourceId=\"0\" isModal=\"true\" hwnd=\"2032262\"/><window name=\"\" class=\"#32770\" resourceId=\"0\" isModal=\"false\" hwnd=\"459658\"/><window name=\"\" class=\"SysTabControl32\" resourceId=\"12320\" isModal=\"false\" hwnd=\"983734\"/>";
+		String target2 = "<window name=\"Messplatz\" class=\"#32770\" resourceId=\"0\" isModal=\"true\" hwnd=\"1770138\"/><window name=\"Messplatz im Einstellmodus\" class=\"#32770\" resourceId=\"0\" isModal=\"true\" hwnd=\"2032262\"/><window name=\"\" class=\"#32770\" resourceId=\"0\" isModal=\"false\" hwnd=\"459658\"/><window name=\"\" class=\"SysTabControl32\" resourceId=\"12320\" isModal=\"false\" hwnd=\"983734\"/>";
+		event.setTarget(target1);
+		boolean expected = true;
+		
+		boolean result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+		
+		MFCTargetComparator.setMutable(false);
+		
+		result = event.targetEquals(target2);
+		
+		assertEquals(expected, result);
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/CommandHelpers.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/CommandHelpers.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/CommandHelpers.java	(revision 432)
@@ -0,0 +1,54 @@
+package de.ugoe.cs.quest;
+
+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("Existing object " + objectName + " overwritten.");
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/ReplayGenerator.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/ReplayGenerator.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/ReplayGenerator.java	(revision 432)
@@ -0,0 +1,174 @@
+package de.ugoe.cs.quest;
+
+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 de.ugoe.cs.quest.IReplayDecorator;
+import de.ugoe.cs.quest.data.IReplayable;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * This class provides the functionality to generate replay files from sequences
+ * if {@link ReplayableEvent}s.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class ReplayGenerator {
+
+	/**
+	 * <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
+	 */
+	public void createLogfileMultipleSessions(
+			Collection<List<ReplayableEvent<?>>> sequences, String filename) {
+		OutputStreamWriter writer = openReplayFile(filename);
+		if (writer != null) {
+			try {
+				decorator = sequences.iterator().next().get(0).getReplayDecorator();
+				if (decorator != null) {
+					writer.write(decorator.getHeader());
+				}
+				for (List<ReplayableEvent<?>> 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>
+	 * Creates a replay file that from a single event sequence.
+	 * </p>
+	 * 
+	 * @param actions
+	 *            event sequence from which the sessions are generated
+	 * @param filename
+	 *            name and path of the replay file
+	 */
+	public void createLogfileSingleSession(List<ReplayableEvent<?>> actions,
+			String filename) {
+		OutputStreamWriter writer = openReplayFile(filename);
+		if (writer != null) {
+			try {
+				actions.get(0).getReplayDecorator();
+				if (decorator != null) {
+					writer.write(decorator.getHeader());
+				}
+				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("Created logfile " + filename);
+			} else {
+				Console.traceln("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<ReplayableEvent<?>> actions,
+			OutputStreamWriter writer) throws IOException {
+		if (decorator != null) {
+			writer.write(decorator.getSessionHeader(sessionId));
+		}
+		for (ReplayableEvent<?> currentAction : actions) {
+
+			List<? extends IReplayable> replayables = currentAction
+					.getReplayMessages();
+			for (IReplayable replayble : replayables) {
+				writer.write(replayble.getReplay() + StringTools.ENDLINE);
+				writer.flush();
+			}
+		}
+		if (decorator != null) {
+			writer.write(decorator.getSessionFooter(sessionId));
+		}
+		sessionId++;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/Runner.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/Runner.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/Runner.java	(revision 432)
@@ -0,0 +1,109 @@
+package de.ugoe.cs.quest;
+
+import java.io.IOException;
+import java.util.List;
+
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import de.ugoe.cs.quest.log4j.Log4JLogger;
+import de.ugoe.cs.quest.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 LOG4JTYPE {
+		enable, disable
+	}
+
+	/**
+	 * <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.quest.commands");
+		CommandExecuter.getInstance().addCommandPackage(
+				"de.ugoe.cs.quest.windows.commands");
+		CommandExecuter.getInstance().addCommandPackage(
+				"de.ugoe.cs.quest.web.commands");
+		CommandExecuter.getInstance().addCommandPackage(
+				"de.ugoe.cs.quest.efg.commands");
+		CommandExecuter.getInstance().addCommandPackage(
+				"de.ugoe.cs.quest.jfc.commands");
+		//new Log4JLogger();
+
+		OptionParser parser = new OptionParser();
+		OptionSpec<LOG4JTYPE> log4j = parser.accepts("log4j", "Allowed values: enable, disable").withRequiredArg()
+				.ofType(LOG4JTYPE.class).defaultsTo(LOG4JTYPE.enable);
+		OptionSpec<UITYPE> ui = parser.accepts("ui", "Allowed values: text, swt").withRequiredArg()
+				.ofType(UITYPE.class).defaultsTo(UITYPE.text);
+		OptionSpec<LOG4JTYPE> trace = parser.accepts("trace", "Allowed values: enable, disable").withRequiredArg().ofType(LOG4JTYPE.class).defaultsTo(LOG4JTYPE.enable);
+		OptionSet options = parser.parse(args);
+
+		List<String> startupCommands = options.nonOptionArguments();
+		try {
+			switch (options.valueOf(log4j)) {
+			case enable:
+				new Log4JLogger();
+				break;
+			case disable:
+				// do nothing
+				break;
+			default:
+				throw new AssertionError("reached source code that should be unreachable");
+			}
+			
+			switch (options.valueOf(ui)) {
+			case text:
+				TextConsole textConsole = new TextConsole();
+				if( options.valueOf(trace)==LOG4JTYPE.disable ) {
+					textConsole.setDebug(false);
+				}
+				for (String command : startupCommands) {
+					CommandExecuter.getInstance().exec(command);
+				}
+				textConsole.run(true);
+				break;
+			case swt:
+				MainWindow mainWindow = new MainWindow(startupCommands);
+				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/quest-ui-core/src/de/ugoe/cs/quest/commands/AbstractTrainCommand.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/AbstractTrainCommand.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/AbstractTrainCommand.java	(revision 432)
@@ -0,0 +1,93 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.util.console.Command;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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<? extends Event<?>>> sequences = (Collection<List<? extends Event<?>>>) dataObject;
+
+		TrieBasedModel model = createModel();
+		model.train(sequences);
+		if (GlobalDataContainer.getInstance().addData(modelname, model)) {
+			CommandHelpers.dataOverwritten(modelname);
+		}
+
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDcalcCoverage.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDcalcCoverage.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDcalcCoverage.java	(revision 432)
@@ -0,0 +1,141 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.coverage.CoverageCalculatorObserved;
+import de.ugoe.cs.quest.coverage.CoverageCalculatorProcess;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		IStochasticProcess process = null;
+		Collection<List<? extends Event<?>>> observedSequences = null;
+		Collection<List<? extends 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<? extends 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<? extends 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 void help() {
+		Console.println("Usage: calcCoverage <modelname> <observedSequences> [sequenceNames] <minCovLength> <maxCovLength>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDcalcEntropy.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDcalcEntropy.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDcalcEntropy.java	(revision 432)
@@ -0,0 +1,64 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		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/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDdeleteObject.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDdeleteObject.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDdeleteObject.java	(revision 432)
@@ -0,0 +1,50 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+		boolean deleted = GlobalDataContainer.getInstance().removeData(
+				objectName);
+		if (!deleted) {
+			CommandHelpers.objectNotFoundMessage(objectName);
+		}
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDflattenModel.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDflattenModel.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDflattenModel.java	(revision 432)
@@ -0,0 +1,72 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.HighOrderMarkovModel;
+import de.ugoe.cs.quest.models.ModelFlattener;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: flattenModel <modelname> <modelname_flattened>");
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateFixedLengthSequences.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateFixedLengthSequences.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateFixedLengthSequences.java	(revision 432)
@@ -0,0 +1,133 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+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.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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;
+		Collection<List<? extends Event<?>>> sequences = new LinkedHashSet<List<? extends 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("" + sequences.size() + " possible");
+		if (!all && numSequences < sequences.size()) {
+			List<Double> probabilities = new ArrayList<Double>(sequences.size());
+			double probSum = 0.0;
+			for (List<? extends 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<? extends Event<?>>> retainedSequences = new LinkedList<List<? extends Event<?>>>();
+			int index = 0;
+			for (List<? extends Event<?>> sequence : sequences) {
+				if (drawnSequences.contains(index)) {
+					retainedSequences.add(sequence);
+				}
+				index++;
+			}
+			sequences = retainedSequences;
+		}
+		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 void help() {
+		Console.println("Usage: generateFixedLengthSequences <modelname> <sequencesName> <minlenght> <maxlength> {<all>} {<numSequences>} {<validEnd>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateGreedy.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateGreedy.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateGreedy.java	(revision 432)
@@ -0,0 +1,179 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+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 de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.coverage.SequenceTools;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.ArrayTools;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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<? extends Event<?>>> allSequences = new LinkedList<List<? extends 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("" + allSequences.size() + " possible");
+
+		Collection<List<? extends Event<?>>> allSubSeqs = model
+				.generateSequences(coverageDepth);
+		Map<List<? extends Event<?>>, Double> weightMap = SequenceTools
+				.generateWeights(model, allSubSeqs);
+		Set<List<? extends Event<?>>> coveredSubSeqs = new LinkedHashSet<List<? extends Event<?>>>();
+
+		List<Set<List<? extends Event<?>>>> containedSubSeqs = new LinkedList<Set<List<? extends Event<?>>>>();
+		for (List<? extends Event<?>> sequence : allSequences) {
+			List<List<? extends Event<?>>> wrapper = new LinkedList<List<? extends Event<?>>>();
+			wrapper.add(sequence);
+			Set<List<? extends Event<?>>> currentSubSeqs = SequenceTools
+					.containedSubSequences(wrapper, coverageDepth);
+			containedSubSeqs.add(currentSubSeqs);
+		}
+
+		List<List<? extends Event<?>>> testSuite = new LinkedList<List<? extends 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<? extends Event<?>>> containedSubSeq : containedSubSeqs) {
+				double gain = 0.0d;
+				Iterator<List<? extends Event<?>>> subSeqIter = containedSubSeq
+						.iterator();
+				while (subSeqIter.hasNext()) {
+					List<? extends 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("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 void help() {
+		Console.println("generateGreedy <modelname> <sequencesName> <minLength> <maxLength> <coverageDepth> <desiredCoverage> {<validEnd>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateHybrid.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateHybrid.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateHybrid.java	(revision 432)
@@ -0,0 +1,180 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+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 de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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;
+		Collection<List<? extends Event<?>>> sequences = new LinkedHashSet<List<? extends Event<?>>>();
+
+		List<List<? extends Event<?>>> seqsTmp = new ArrayList<List<? extends Event<?>>>(
+				model.generateSequences(maxLengthAll + 1, true));
+
+		Console.traceln("" + seqsTmp.size() + " of length " + maxLengthAll
+				+ " possible");
+		List<Double> probabilities = new ArrayList<Double>(seqsTmp.size());
+		double probSum = 0.0;
+		for (List<? extends 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<? extends Event<?>> seqTmp = seqsTmp.get(index);
+			if (!Event.ENDEVENT.equals(seqTmp.get(seqTmp.size() - 1))) {
+				List<? extends 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);
+				}
+			}
+		}
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+		Console.println("" + sequences.size() + " sequences generated");
+	}
+
+	public List<? extends Event<?>> finishSequence(
+			List<? extends Event<?>> sequence, IStochasticProcess model,
+			int maxLength, 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() <= maxLength) {
+				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() == maxLength);
+						break;
+					}
+				}
+			}
+		}
+		if( iter==maxIter ) {
+			return null;
+		}
+		return sequenceCopy;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public void help() {
+		Console.println("Usage: generateHybrid <modelname> <sequencesName> <lenght> <maxlengthAll> <numSequences> {<validEnd>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateRandomReplay.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateRandomReplay.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateRandomReplay.java	(revision 432)
@@ -0,0 +1,82 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.ReplayGenerator;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command to create a replay file with randomly generated sessions.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDgenerateRandomReplay implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public void help() {
+		Console.println("Usage: generateRandomReplay <modelName> <filename> {<numSessions>}");
+	}
+
+	/*
+	 * (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 filename;
+		int numSessions = 1;
+		try {
+			modelname = (String) parameters.get(0);
+			filename = (String) parameters.get(1);
+			if (parameters.size() < 3) {
+				numSessions = Integer.parseInt((String) parameters.get(2));
+			}
+		} catch (Exception e) {
+			throw new InvalidParameterException();
+		}
+
+		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;
+		Collection<List<ReplayableEvent<?>>> sequences = new LinkedList<List<ReplayableEvent<?>>>();
+		try {
+			for (int i = 0; i < numSessions; i++) {
+				sequences
+						.add((List<ReplayableEvent<?>>) model.randomSequence());
+			}
+		} catch (ClassCastException e) {
+			Console.printerrln("Modeled events don't support replay.");
+		}
+		ReplayGenerator generator = new ReplayGenerator();
+		generator.createLogfileMultipleSessions(sequences, filename);
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateRandomSequences.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateRandomSequences.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateRandomSequences.java	(revision 432)
@@ -0,0 +1,105 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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)
+	 */
+	@SuppressWarnings("unchecked")
+	@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 InvalidParameterException();
+		}
+
+		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;
+		Set<List<? extends Event<?>>> sequences = new HashSet<List<? extends Event<?>>>(
+				numSessions);
+		long numIterations = 0;
+		while (sequences.size() < numSessions && numIterations < maxIter) {
+			List<? extends 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);
+			}
+			numIterations++;
+		}
+		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 void help() {
+		Console.println("Usage: generateRandomSequenecs <modelName> <sequencesName> <numSessions> <minLength> <maxLength> {<maxIter>} {<validEnd>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateReplayfile.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateReplayfile.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDgenerateReplayfile.java	(revision 432)
@@ -0,0 +1,70 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.ReplayGenerator;
+import de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command to create a replay file from stored sessions.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class CMDgenerateReplayfile implements Command {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		Collection<List<ReplayableEvent<?>>> 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<ReplayableEvent<?>>>) dataObject;
+		ReplayGenerator generator = new ReplayGenerator();
+		generator.createLogfileMultipleSessions(sequences, filename);
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDlistSymbols.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDlistSymbols.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDlistSymbols.java	(revision 432)
@@ -0,0 +1,73 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Arrays;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: listStates <modelName> {<sort>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDload.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDload.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDload.java	(revision 432)
@@ -0,0 +1,61 @@
+package de.ugoe.cs.quest.commands;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: load <filename>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDloadObject.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDloadObject.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDloadObject.java	(revision 432)
@@ -0,0 +1,69 @@
+package de.ugoe.cs.quest.commands;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: loadObject <filename> <objectName>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDmodelSize.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDmodelSize.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDmodelSize.java	(revision 432)
@@ -0,0 +1,63 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: modelSize <modelName>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDprintDot.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDprintDot.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDprintDot.java	(revision 432)
@@ -0,0 +1,63 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.IDotCompatible;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		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/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDprintTrieDot.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDprintTrieDot.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDprintTrieDot.java	(revision 432)
@@ -0,0 +1,64 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.Trie;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		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/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsave.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsave.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsave.java	(revision 432)
@@ -0,0 +1,59 @@
+package de.ugoe.cs.quest.commands;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: save <filename>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsaveObject.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsaveObject.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsaveObject.java	(revision 432)
@@ -0,0 +1,70 @@
+package de.ugoe.cs.quest.commands;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: saveObject <filename> <objectName>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsequenceStatistics.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsequenceStatistics.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDsequenceStatistics.java	(revision 432)
@@ -0,0 +1,83 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+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.quest.CommandHelpers;
+import de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Command to print basic statistical information about stored sequences, e.g.,
+ * how many there are of which lenght.
+ * </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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: sequenceStatistics <sequencesName>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowMarkovModel.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowMarkovModel.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowMarkovModel.java	(revision 432)
@@ -0,0 +1,114 @@
+package de.ugoe.cs.quest.commands;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import javax.swing.JFrame;
+
+import org.apache.commons.collections15.Transformer;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel.MarkovEdge;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import edu.uci.ics.jung.algorithms.layout.Layout;
+import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
+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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		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/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowSequences.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowSequences.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowSequences.java	(revision 432)
@@ -0,0 +1,69 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.swt.SequencesDialog;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		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/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowTimer.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowTimer.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowTimer.java	(revision 432)
@@ -0,0 +1,60 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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("" + (currentTime - startTime) + " milliseconds");
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public void help() {
+		Console.println("Usage: showTimer <timerName>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowTrie.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowTrie.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDshowTrie.java	(revision 432)
@@ -0,0 +1,101 @@
+package de.ugoe.cs.quest.commands;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import javax.swing.JFrame;
+
+import org.apache.commons.collections15.Transformer;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.Trie;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.quest.models.Trie.Edge;
+import de.ugoe.cs.quest.models.Trie.TrieVertex;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		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/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstartFileListener.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstartFileListener.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstartFileListener.java	(revision 432)
@@ -0,0 +1,53 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.FileOutputListener;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: startFileListener <filename>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstartTimer.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstartTimer.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstartTimer.java	(revision 432)
@@ -0,0 +1,50 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+		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 void help() {
+		Console.println("Usage: startTimer <timerName>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstopFileListener.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstopFileListener.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDstopFileListener.java	(revision 432)
@@ -0,0 +1,51 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.FileOutputListener;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Command: stopFileListener <filename>");
+
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainDFA.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainDFA.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainDFA.java	(revision 432)
@@ -0,0 +1,52 @@
+package de.ugoe.cs.quest.commands;
+
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.models.DeterministicFiniteAutomaton;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: trainDFA <modelName> <sequencesName>");
+	}
+
+	/**
+	 * <p>
+	 * No additional parameters.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.quest.commands.AbstractTrainCommand#handleAdditionalParameters(java.util.List)
+	 */
+	@Override
+	void handleAdditionalParameters(List<Object> parameters) throws Exception {
+		// no additional parameters.
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.commands.AbstractTrainCommand#createModel()
+	 */
+	@Override
+	TrieBasedModel createModel() {
+		return new DeterministicFiniteAutomaton(new Random());
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainMarkovModel.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainMarkovModel.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainMarkovModel.java	(revision 432)
@@ -0,0 +1,68 @@
+package de.ugoe.cs.quest.commands;
+
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.HighOrderMarkovModel;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: trainMarkovModel <modelName> <sequencesName> {<order>}");
+	}
+
+	/**
+	 * <p>
+	 * Handles the parameter order.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.quest.commands.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.quest.commands.AbstractTrainCommand#createModel()
+	 */
+	@Override
+	TrieBasedModel createModel() {
+		if (order == 1) {
+			return new FirstOrderMarkovModel(new Random());
+		} else {
+			return new HighOrderMarkovModel(order, new Random());
+		}
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainPPM.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainPPM.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDtrainPPM.java	(revision 432)
@@ -0,0 +1,78 @@
+package de.ugoe.cs.quest.commands;
+
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.models.PredictionByPartialMatch;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: trainPPM <modelName> <sequencesName> <probEscape> <maxOrder> {<minOrder>}");
+	}
+
+	/**
+	 * <p>
+	 * Handles the parameters probEscape, maxOrder, and minOrder.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.quest.commands.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.quest.commands.AbstractTrainCommand#createModel()
+	 */
+	@Override
+	TrieBasedModel createModel() {
+		return new PredictionByPartialMatch(maxOrder, minOrder, new Random(),
+				probEscape);
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDupdateModel.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDupdateModel.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/commands/CMDupdateModel.java	(revision 432)
@@ -0,0 +1,80 @@
+package de.ugoe.cs.quest.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.TrieBasedModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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<? extends Event<?>>> sequences = (Collection<List<? extends 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 void help() {
+		Console.println("Usage: updateModel <modelname> <sequencesName>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/data/GlobalDataContainer.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/data/GlobalDataContainer.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/data/GlobalDataContainer.java	(revision 432)
@@ -0,0 +1,226 @@
+package de.ugoe.cs.quest.data;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import de.ugoe.cs.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+
+/**
+ * <p>
+ * This data structure can be used by the commands to store any {@link Object}.
+ * The data is stored in a key-value map, with strings as keys.
+ * </p>
+ * <p>
+ * This class is implemented as a singleton, as more than one data container
+ * does not serves no purpose.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class GlobalDataContainer implements Serializable {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * Instance of the {@link GlobalDataContainer} (implemented as singleton).
+	 * </p>
+	 */
+	transient private static GlobalDataContainer theInstance = null;
+
+	/**
+	 * <p>
+	 * Internal storage of the data.
+	 * </p>
+	 */
+	private Map<String, Object> dataObjects;
+
+	/**
+	 * <p>
+	 * Returns the instance of the container. If it does not yet exist, the data
+	 * container is created.
+	 * </p>
+	 * 
+	 * @return instance of the container
+	 */
+	public static GlobalDataContainer getInstance() {
+		if (theInstance == null) {
+			theInstance = new GlobalDataContainer();
+		}
+		return theInstance;
+	}
+
+	/**
+	 * <p>
+	 * Manual serialization of the object. Necessary to guarantee the singleton
+	 * property.
+	 * </p>
+	 * 
+	 * @param s
+	 *            output stream for the serialization
+	 * @throws IOException
+	 *             thrown if there is problem writing to the output stream
+	 */
+	private void writeObject(ObjectOutputStream s) throws IOException {
+		s.defaultWriteObject();
+		s.writeObject(dataObjects);
+	}
+
+	/**
+	 * <p>
+	 * Manual de-serialization of the object. Necessary to guarantee the
+	 * singleton property.
+	 * 
+	 * @param s
+	 *            input stream for the de-serialization
+	 * @throws IOException
+	 *             thrown if there is problem reading from the input stream
+	 * @throws ClassNotFoundException
+	 *             thrown if there is a problem reading from the input stream
+	 */
+	@SuppressWarnings("unchecked")
+	private void readObject(ObjectInputStream s) throws IOException,
+			ClassNotFoundException {
+		s.defaultReadObject();
+		if (theInstance == null) {
+			theInstance = new GlobalDataContainer();
+		}
+		theInstance.dataObjects = (Map<String, Object>) s.readObject();
+	}
+
+	/**
+	 * <p>
+	 * Manual de-serialization to guarantee the singleton property.
+	 * </p>
+	 * 
+	 * @return instance of the container
+	 */
+	private Object readResolve() {
+		return theInstance;
+	}
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new GlobalDataContainer. Private to guarantee the
+	 * singleton property.
+	 * </p>
+	 */
+	private GlobalDataContainer() {
+		dataObjects = new HashMap<String, Object>();
+	}
+
+	/**
+	 * <p>
+	 * Adds data to the container.
+	 * </p>
+	 * 
+	 * @param key
+	 *            key that identifies the data
+	 * @param data
+	 *            data that is stored
+	 * @return true, if an old entry was overwritten; false otherwise
+	 */
+	public boolean addData(String key, Object data) {
+		Object previousEntry = dataObjects.put(key, data);
+		return previousEntry != null;
+	}
+
+	/**
+	 * <p>
+	 * Removes data from the container.
+	 * </p>
+	 * 
+	 * @param key
+	 *            key of the data to be removed
+	 * @return true, if the object was removed; false if it was not present
+	 */
+	public boolean removeData(String key) {
+		Object previousEntry = dataObjects.remove(key);
+		return previousEntry != null;
+	}
+
+	/**
+	 * <p>
+	 * Returns the data associated with a key or {@code null} if no data is
+	 * stored for the key.
+	 * </p>
+	 * 
+	 * @param key
+	 *            key whose data is returned
+	 * @return data associated with the key; {@code null} if no data is
+	 *         available
+	 */
+	public Object getData(String key) {
+		return dataObjects.get(key);
+	}
+
+	/**
+	 * <p>
+	 * Resets the data container, i.e., deletes all its contents.
+	 * </p>
+	 */
+	public void reset() {
+		dataObjects = new HashMap<String, Object>();
+	}
+
+	/**
+	 * <p>
+	 * Returns all keys of collections of sequences contained in the storage.
+	 * </p>
+	 * 
+	 * @return keys of all collections of sequences contained in the storage
+	 */
+	public Collection<String> getAllSequencesNames() {
+		Collection<String> allSequencesNames = new LinkedList<String>();
+		for (Entry<String, Object> entry : dataObjects.entrySet()) {
+			if( SequenceInstanceOf.isCollectionOfSequences(entry.getValue())) {
+				allSequencesNames.add(entry.getKey());
+			}
+		}
+		return allSequencesNames;
+	}
+
+	/**
+	 * <p>
+	 * Returns the keys of all {@link IStochasticProcess}s contained in the
+	 * storage.
+	 * </p>
+	 * 
+	 * @return keys of all {@link IStochasticProcess}s contained in the storage
+	 */
+	public Collection<String> getAllModelNames() {
+		Collection<String> modelNames = new LinkedList<String>();
+		for (Entry<String, Object> entry : dataObjects.entrySet()) {
+			if (entry.getValue() instanceof IStochasticProcess) {
+				modelNames.add(entry.getKey());
+			}
+		}
+		return modelNames;
+	}
+
+	/**
+	 * <p>
+	 * Returns the keys of all objects contained in the storage.
+	 * </p>
+	 * 
+	 * @return keys of all objects in the storage
+	 */
+	public Collection<String> getAllKeys() {
+		return dataObjects.keySet();
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/EFGModelGenerator.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/EFGModelGenerator.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/EFGModelGenerator.java	(revision 432)
@@ -0,0 +1,132 @@
+package de.ugoe.cs.quest.efg;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import de.ugoe.cs.quest.commands.CMDupdateModel;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.efg.data.EFGEvent;
+import de.ugoe.cs.quest.models.DeterministicFiniteAutomaton;
+import de.ugoe.cs.quest.models.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.quest.models 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}. However, through further
+	 * training (e.g., {@link CMDupdateModel}) this can be changed.
+	 * </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<? extends 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<? extends 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<? extends 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<? extends Event<?>>> subsequences = new LinkedList<List<? extends 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.
+			 */
+			Event<?> myEvent = new EFGEvent(event.getEventId());
+			myEvent.setTarget(event.getWidgetId());
+			myEvents.add(myEvent);
+		}
+		return myEvents;
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/EFGReplayDecorator.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/EFGReplayDecorator.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/EFGReplayDecorator.java	(revision 432)
@@ -0,0 +1,95 @@
+package de.ugoe.cs.quest.efg;
+
+import de.ugoe.cs.quest.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.quest.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.quest.IReplayDecorator#getFooter()
+	 */
+	@Override
+	public String getFooter() {
+		return "</TestCase>";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.IReplayDecorator#getSessionHeader(int)
+	 */
+	@Override
+	public String getSessionHeader(int sessionId) {
+		return "";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.IReplayDecorator#getSessionFooter(int)
+	 */
+	@Override
+	public String getSessionFooter(int sessionId) {
+		return "";
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/GUITARTestCaseParser.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/GUITARTestCaseParser.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/GUITARTestCaseParser.java	(revision 432)
@@ -0,0 +1,109 @@
+package de.ugoe.cs.quest.efg;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.efg.data.EFGEvent;
+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();
+			EFGEvent event = new EFGEvent(eventId);
+			event.setTarget(getWidgetId(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/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgTestCasesToSequences.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgTestCasesToSequences.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgTestCasesToSequences.java	(revision 432)
@@ -0,0 +1,83 @@
+package de.ugoe.cs.quest.efg.commands;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.efg.GUITARTestCaseParser;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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("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 void help() {
+		Console.println("Usage: efgTestCasesToSequences <folder> <sequencesName> {<efgFileName>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgToDFA.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgToDFA.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgToDFA.java	(revision 432)
@@ -0,0 +1,55 @@
+package de.ugoe.cs.quest.efg.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.efg.EFGModelGenerator;
+import de.ugoe.cs.quest.models.DeterministicFiniteAutomaton;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: efgToDFA <filename> <modelname>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgToMM.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgToMM.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/commands/CMDefgToMM.java	(revision 432)
@@ -0,0 +1,55 @@
+package de.ugoe.cs.quest.efg.commands;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.efg.EFGModelGenerator;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		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 void help() {
+		Console.println("Usage: efgToMM <filename> <modelname>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/data/EFGEvent.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/data/EFGEvent.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/data/EFGEvent.java	(revision 432)
@@ -0,0 +1,34 @@
+package de.ugoe.cs.quest.efg.data;
+
+import de.ugoe.cs.quest.data.ReplayableEvent;
+
+/**
+ * <p>
+ * Convenience class for working with EFGEvents.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class EFGEvent extends ReplayableEvent<EFGReplayable> {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new EFGEvent.
+	 * </p>
+	 * 
+	 * @param eventId
+	 *            EventId of the event in the EFG and GUI files
+	 */
+	public EFGEvent(String eventId) {
+		super(eventId);
+		this.addReplayEvent(new EFGReplayable(eventId));
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/data/EFGReplayable.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/data/EFGReplayable.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/efg/data/EFGReplayable.java	(revision 432)
@@ -0,0 +1,66 @@
+package de.ugoe.cs.quest.efg.data;
+
+import de.ugoe.cs.quest.data.IReplayable;
+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 EFGReplayable 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 EFGReplayable}.
+	 * </p>
+	 * 
+	 * @param eventId
+	 */
+	public EFGReplayable(String eventId) {
+		this.eventId = eventId;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.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.quest.data.IReplayable#getTarget()
+	 */
+	@Override
+	public String getTarget() {
+		return null; // target indirectly included in replay through the eventId
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/JFCLogParser.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/JFCLogParser.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/JFCLogParser.java	(revision 432)
@@ -0,0 +1,281 @@
+package de.ugoe.cs.quest.jfc;
+
+import java.awt.event.FocusEvent;
+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.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+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.quest.jfc.data.JFCEvent;
+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<JFCEvent>> sequences;
+
+	/**
+	 * <p>
+	 * Internal handle to the event that is currently being parsed.
+	 * </p>
+	 */
+	private JFCEvent currentEvent;
+
+	/**
+	 * <p>
+	 * Internal handle to the event sequence that is currently being parsed.
+	 * </p>
+	 */
+	private List<JFCEvent> currentSequence = null;
+
+	/**
+	 * <p>
+	 * Internally used string to build a string representing a component-node.
+	 * </p>
+	 */
+	private String componentString;
+
+	/**
+	 * <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>
+	 */
+	ParamSource paramSource = null;
+
+	private Collection<Integer> 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<JFCEvent>>();
+		currentSequence = new LinkedList<JFCEvent>();
+		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<Integer> ignoredEvents) {
+		sequences = new LinkedList<List<JFCEvent>>();
+		currentSequence = new LinkedList<JFCEvent>();
+		eventFilter = ignoredEvents;
+	}
+
+	/**
+	 * <p>
+	 * creates a default event filter that ignores focus changes, mouse pressed
+	 * and mouse released events.
+	 * </p>
+	 */
+	private void setupDefaultEventFilter() {
+		eventFilter = new HashSet<Integer>();
+		eventFilter.add(MouseEvent.MOUSE_PRESSED);
+		eventFilter.add(MouseEvent.MOUSE_RELEASED);
+		eventFilter.add(FocusEvent.FOCUS_GAINED);
+	}
+
+	/**
+	 * <p>
+	 * Returns the collection of event sequences that is obtained from parsing
+	 * log files.
+	 * </p>
+	 * 
+	 * @return collection of event sequences
+	 */
+	public Collection<List<JFCEvent>> getSequences() {
+		return sequences;
+	}
+
+	/*
+	 * (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<JFCEvent>();
+		}
+		if (qName.equals("newsession")) {
+			Console.traceln("start of session");
+			if (currentSequence != null && !currentSequence.isEmpty()) {
+				sequences.add(currentSequence);
+			}
+			currentSequence = new LinkedList<JFCEvent>();
+		} else if (qName.equals("event")) {
+			int eventId = Integer.parseInt(atts.getValue("id"));
+			if ((eventFilter == null) || (!eventFilter.contains(eventId))) {
+				currentEvent = new JFCEvent(atts.getValue("id"));
+				paramSource = ParamSource.EVENT;
+			}
+		} else if (currentEvent != null) {
+			if (qName.equals("param")) {
+				if (paramSource == ParamSource.EVENT) {
+					currentEvent.addParameter(atts.getValue("name"),
+							atts.getValue("value"));
+				} else if (paramSource == ParamSource.SOURCE) {
+					currentEvent.addSourceInformation(atts.getValue("name"),
+							atts.getValue("value"));
+				} else if (paramSource == ParamSource.PARENT) {
+					currentEvent.addParentInformation(atts.getValue("name"),
+							atts.getValue("value"));
+				} else if (paramSource == ParamSource.COMPONENT) {
+					componentString += "'" + atts.getValue("value") + "',";
+				}
+			} else if (qName.equals("source")) {
+				paramSource = ParamSource.SOURCE;
+			} else if (qName.equals("parent")) {
+				paramSource = ParamSource.PARENT;
+			} else if (qName.equals("component")) {
+				paramSource = ParamSource.COMPONENT;
+				componentString = "[";
+			}
+		}
+	}
+
+	/*
+	 * (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 (currentEvent != null) {
+			if (qName.equals("event")) {
+				currentSequence.add(currentEvent);
+				currentEvent = 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);
+				currentEvent.extendTarget(componentString.substring(0,
+						componentString.length() - 1) + "]");
+			}
+		}
+	}
+
+	/**
+	 * <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 InvalidParameterException("filename 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(filename), "UTF-8"));
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		} catch (SAXException e) {
+			e.printStackTrace();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+		if (inputSource != null) {
+			inputSource.setSystemId("file://"
+					+ new File(filename).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()
+						+ ".");
+				e.printStackTrace();
+			} catch (SAXException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDparseDirJFC.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDparseDirJFC.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDparseDirJFC.java	(revision 432)
@@ -0,0 +1,92 @@
+package de.ugoe.cs.quest.jfc.commands;
+
+import java.io.File;
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.jfc.JFCLogParser;
+import de.ugoe.cs.quest.jfc.data.JFCEvent;
+import de.ugoe.cs.quest.jfc.data.JFCTargetComparator;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+
+		File folder = new File(path);
+		if (!folder.isDirectory()) {
+			Console.printerrln(path + " is not a directory");
+			return;
+		}
+
+		JFCTargetComparator.setMutable(true);
+		JFCLogParser parser = new JFCLogParser();
+
+		String absolutPath = folder.getAbsolutePath();
+		for (String filename : folder.list()) {
+			String source = absolutPath + "/" + filename;
+			Console.traceln("Processing file: " + source);
+
+			parser.parseFile(source);
+		}
+
+		Collection<List<JFCEvent>> sequences = parser.getSequences();
+		Console.traceln("Pre-computing event target equalities.");
+		// compare all Events to a dummy event to make sure they are known by
+		// the JFCTargetComparator
+		JFCEvent dummyEvent = new JFCEvent("dummy");
+		for (List<JFCEvent> sequence : sequences) {
+			for (JFCEvent event : sequence) {
+				event.equals(dummyEvent);
+			}
+		}
+		JFCTargetComparator.setMutable(false);
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public void help() {
+		Console.println("Usage: parseDirJFC <path> {<sequencesName>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDparseJFC.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDparseJFC.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDparseJFC.java	(revision 432)
@@ -0,0 +1,76 @@
+package de.ugoe.cs.quest.jfc.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.jfc.JFCLogParser;
+import de.ugoe.cs.quest.jfc.data.JFCEvent;
+import de.ugoe.cs.quest.jfc.data.JFCTargetComparator;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+		JFCTargetComparator.setMutable(true);
+		JFCLogParser parser = new JFCLogParser();
+
+		parser.parseFile(filename);
+		Collection<List<JFCEvent>> sequences = parser.getSequences();
+
+		Console.traceln("Pre-computing event target equalities.");
+		// compare all Events to a dummy event to make sure they are known by
+		// the JFCTargetComparator
+		JFCEvent dummyEvent = new JFCEvent("dummy");
+		for (List<JFCEvent> sequence : sequences) {
+			for (JFCEvent event : sequence) {
+				event.equals(dummyEvent);
+			}
+		}
+		JFCTargetComparator.setMutable(false);
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.util.console.Command#help()
+	 */
+	@Override
+	public void help() {
+		Console.println("Usage: parseJFC <filename> {<sequencesName>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDpreprocessDirJFC.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDpreprocessDirJFC.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDpreprocessDirJFC.java	(revision 432)
@@ -0,0 +1,119 @@
+package de.ugoe.cs.quest.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.security.InvalidParameterException;
+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 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 InvalidParameterException();
+		}
+
+		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("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("   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 void help() {
+		Console.println("Usage: preprocessDirJFC <sourcePath> <targetPath>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDpreprocessJFC.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDpreprocessJFC.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/commands/CMDpreprocessJFC.java	(revision 432)
@@ -0,0 +1,98 @@
+package de.ugoe.cs.quest.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.security.InvalidParameterException;
+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 InvalidParameterException();
+		}
+
+		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();
+
+		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 void help() {
+		Console.println("Usage: preprocessJFC <sourceFile> <targetFile>");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/data/JFCEvent.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/data/JFCEvent.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/data/JFCEvent.java	(revision 432)
@@ -0,0 +1,235 @@
+package de.ugoe.cs.quest.jfc.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import de.ugoe.cs.quest.data.IReplayable;
+import de.ugoe.cs.quest.data.ReplayableEvent;
+import de.ugoe.cs.quest.jfc.JFCLogParser;
+
+/**
+ * <p>
+ * This class defines JFC events.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class JFCEvent extends ReplayableEvent<IReplayable> {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * Internal map of parameters associated with the event.
+	 * </p>
+	 */
+	private Map<String, String> parameters;
+
+	/**
+	 * <p>
+	 * Information about the event source.
+	 * </p>
+	 */
+	private Map<String, String> sourceParameters;
+
+	/**
+	 * <p>
+	 * Information about the parent of the event source.
+	 * </p>
+	 */
+	private Map<String, String> parentParameters;
+	
+	private boolean targetChanged = false;
+	
+	private int targetHash = -1;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new JFCEvent.
+	 * </p>
+	 * 
+	 * @param type
+	 *            type of the event
+	 */
+	public JFCEvent(String type) {
+		super(type);
+		parameters = new HashMap<String, String>();
+		sourceParameters = new HashMap<String, String>();
+		parentParameters = new HashMap<String, String>();
+	}
+
+	/**
+	 * <p>
+	 * Adds a new parameter to the event.
+	 * </p>
+	 * 
+	 * @param name
+	 *            name of the parameter
+	 * @param value
+	 *            value of the parameter
+	 */
+	public void addParameter(String name, String value) {
+		parameters.put(name, value);
+	}
+
+	/**
+	 * <p>
+	 * Retrieves the value of a parameter.
+	 * </p>
+	 * 
+	 * @param name
+	 *            name of the parameter
+	 * @return value of the parameter
+	 */
+	public String getParameter(String name) {
+		return parameters.get(name);
+	}
+
+	/**
+	 * <p>
+	 * Adds new information about the source of the event.
+	 * </p>
+	 * 
+	 * @param name
+	 *            name of the information
+	 * @param value
+	 *            value of the information
+	 */
+	public void addSourceInformation(String name, String value) {
+		sourceParameters.put(name, value);
+	}
+
+	/**
+	 * <p>
+	 * Retrieves information about the source of the event.
+	 * </p>
+	 * 
+	 * @param name
+	 *            name of the information
+	 * @return value of the information
+	 */
+	public String getSourceInformation(String name) {
+		return sourceParameters.get(name);
+	}
+
+	/**
+	 * <p>
+	 * Adds new information about the parent of the source of the event.
+	 * </p>
+	 * 
+	 * @param name
+	 *            name of the information
+	 * @param value
+	 *            value of the information
+	 */
+	public void addParentInformation(String name, String value) {
+		parentParameters.put(name, value);
+	}
+
+	/**
+	 * <p>
+	 * Used by the {@link JFCLogParser} to extend the target string of the
+	 * current event with a further ancestor. The resulting target string will
+	 * have the structure {@code etc.grandparent.parent.eventtarget}.
+	 * </p>
+	 * 
+	 * @param extension
+	 *            extension for the target.
+	 */
+	public void extendTarget(String extension) {
+		if (target == null || "".equals(target)) {
+			target = extension;
+		} else {
+			target += "." + extension;
+		}
+		targetChanged = true;
+	}
+
+	/**
+	 * <p>
+	 * Retrieves information about the parent of the source of the event.
+	 * </p>
+	 * 
+	 * @param name
+	 *            name of the information
+	 * @return value of the information
+	 */
+	public String getParentInformation(String name) {
+		return parentParameters.get(name);
+	}
+
+	/**
+	 * <p>
+	 * This method implements the comparison between two targets of JFCEvents.
+	 * The targets are equal, if they have the same placement in the widget
+	 * hierarchy, i.e., the target strings describe the same widgets, according
+	 * to the implementation of widget equality provided by
+	 * {@link #compareWidgets(String, String)}.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.quest.data.Event#targetEquals(java.lang.String)
+	 */
+	@Override
+	protected boolean targetEquals(String otherTarget) {
+		return JFCTargetComparator.compare(target, otherTarget);
+	}
+
+	/**
+	 * <p>
+	 * The targetHashCode ignores the parts of the target that describe the
+	 * title and hash of a widget, to ensure that the equals/hashCode contract
+	 * is fulfilled.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.quest.data.Event#targetHashCode()
+	 */
+	@Override
+	protected int targetHashCode() {
+		if( targetChanged || targetHash==-1 ) {
+			targetHash = 0;
+			int multiplier = 29;
+			if (target != null) {
+				String[] targetParts = target.split("\\]\\.\\[");
+				if (targetParts.length == 0) {
+					targetHash = widgetHashCode(target);
+				} else {
+					for (String widgetString : targetParts) {
+						targetHash = targetHash * multiplier
+								+ widgetHashCode(widgetString);
+					}
+				}
+			}
+			targetChanged = false;
+		}
+		return targetHash;
+	}
+
+	/**
+	 * <p>
+	 * This method calculates the hashCode for a a widget. If is used by
+	 * {@link #targetHashCode()} to build the complete hashCode.
+	 * </p>
+	 * 
+	 * @param widget
+	 *            string describing the widget
+	 * @return hashCode of the widget
+	 */
+	private int widgetHashCode(String widget) {
+		int hashCode = 0;
+		int multiplier = 37;
+		String[] widgetInfo = widget.split("','");
+		if (widgetInfo.length == 5) {
+			hashCode = hashCode * multiplier + widgetInfo[1].hashCode();
+			hashCode = hashCode * multiplier + widgetInfo[2].hashCode();
+			//hashCode = hashCode * multiplier + widgetInfo[3].hashCode();
+		}
+		return hashCode;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/data/JFCTargetComparator.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/data/JFCTargetComparator.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/jfc/data/JFCTargetComparator.java	(revision 432)
@@ -0,0 +1,429 @@
+package de.ugoe.cs.quest.jfc.data;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections15.CollectionUtils;
+
+/**
+ * <p>
+ * This class implements a comparator for target string for JFC GUIs. It
+ * internally maintains a collection of all targets that have been compared, to
+ * ensure transitivity of the equals relation. This memory can always be deleted
+ * by calling {@link #reset()}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class JFCTargetComparator {
+
+	/**
+	 * <p>
+	 * If mutable is true, new target strings can be added to the internal
+	 * memory. This leads to a very expensive {@link #compare(String, String)}
+	 * operation.
+	 * </p>
+	 * <p>
+	 * if mutable is set to false, currently possible equal targets are
+	 * pre-computed. This pre-computation is expensive and might take a while.
+	 * In turn, the {@link #compare(String, String)} operation becomes very
+	 * cheap.
+	 * </p>
+	 */
+	private static boolean mutable = true;
+
+	/**
+	 * <p>
+	 * Set of all currently known targets.
+	 * </p>
+	 */
+	private static Set<String> knownTargets = new LinkedHashSet<String>();
+
+	/**
+	 * <p>
+	 * Map that contains for all known target strings all equal target strings.
+	 * Pre-computed when {@link #mutable} is set to false.
+	 * </p>
+	 */
+	private static Map<String, Set<String>> equalTargets;
+
+	/**
+	 * <p>
+	 * Changes the mutability of the comparator. If the mutability is changed
+	 * from true to false, the map {@link #equalTargets} is computed.
+	 * </p>
+	 * 
+	 * @param mutable
+	 *            new mutability of the comparator
+	 */
+	public static void setMutable(boolean mutable) {
+		if (JFCTargetComparator.mutable == true && mutable == false) {
+			equalTargets = new HashMap<String, Set<String>>();
+			for (String target1 : knownTargets) {
+				Set<String> curEqualTargets = new HashSet<String>();
+				for (String target2 : knownTargets) {
+					if (compare(target1, target2)) {
+						curEqualTargets.add(target2);
+					}
+				}
+				equalTargets.put(target1, curEqualTargets);
+			}
+		}
+		JFCTargetComparator.mutable = mutable;
+	}
+
+	/**
+	 * <p>
+	 * Compares to target strings. The strings are equal, if
+	 * <ul>
+	 * <li>the class, index, and text of all widgets are equal</li>
+	 * <li>either the title or the hashCode of all widgets are equal</li>
+	 * <li>either the title or the hashCode has been observed in one equal
+	 * instance of a widget, for all widgets.</li>
+	 * </ul>
+	 * </p>
+	 * <p>
+	 * All target strings are remembered internally, to be able to test for the
+	 * third property.
+	 * </p>
+	 * 
+	 * @param target1
+	 *            first target string
+	 * @param target2
+	 *            second target string
+	 * @return true, if both targets are equal; false otherwise
+	 */
+	public static boolean compare(String target1, String target2) {
+		boolean result = false;
+		if (mutable) {
+			instance.addTarget(target1);
+			instance.addTarget(target2);
+			knownTargets.add(target1);
+			knownTargets.add(target2);
+			JFCWidget widget1 = instance.find(target1);
+			JFCWidget widget2 = instance.find(target2);
+			result = (widget1 == widget2);
+		}
+
+		if (!mutable) {
+			Set<String> curEquals = equalTargets.get(target1);
+			if (curEquals != null) {
+				result = curEquals.contains(target2);
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * <p>
+	 * Resets the internal memory of targets.
+	 * </p>
+	 */
+	public static void reset() {
+		instance = new JFCTargetComparator();
+	}
+
+	/**
+	 * <p>
+	 * Internal handle to the instance of this class (implemented as
+	 * Singleton!).
+	 * </p>
+	 */
+	private static JFCTargetComparator instance = new JFCTargetComparator();
+
+	/**
+	 * <p>
+	 * Private Constructor. Creates a new instance of the class and prevents
+	 * instantiation from outside of this class.
+	 * </p>
+	 */
+	private JFCTargetComparator() {
+	}
+
+	/**
+	 * <p>
+	 * List of the root widgets found in the target string.
+	 * </p>
+	 */
+	private List<JFCWidget> rootWidgets = new ArrayList<JFCTargetComparator.JFCWidget>();
+
+	/**
+	 * <p>
+	 * Adds a target to the memory.
+	 * </p>
+	 * 
+	 * @param target
+	 *            target to be added
+	 */
+	private void addTarget(String target) {
+		if (target != null) {
+			String[] targetParts = target.split("\\]\\.\\[");
+			if (targetParts.length == 1) {
+				addWidget(target.substring(1, target.length() - 1), null);
+			} else {
+				JFCWidget parent = null;
+				for (int i = 0; i < targetParts.length; i++) {
+					if (i == 0) {
+						parent = addWidget(targetParts[i].substring(1), parent);
+					} else if (i == targetParts.length - 1) {
+						parent = addWidget(
+								targetParts[i].substring(0,
+										targetParts[i].length() - 1), parent);
+					} else {
+						parent = addWidget(targetParts[i], parent);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * <p>
+	 * Adds a widget extracted from a target to the memory. The widget is placed
+	 * as a child/parent of other widget according to the GUI hierarchy of the
+	 * application.
+	 * </p>
+	 * <p>
+	 * In case the widget already exists, the existing widget is returned and
+	 * the known targets and hashCodes of the existing widget are updated.
+	 * </p>
+	 * 
+	 * @param widgetString
+	 *            string identifying the widget
+	 * @param parent
+	 *            parent widget; if null, it is a root widget and added to
+	 *            {@link #rootWidgets}
+	 * @return the created widget.
+	 */
+	private JFCWidget addWidget(String widgetString, JFCWidget parent) {
+		String[] widgetInfo = widgetString.split("','");
+		JFCWidget widget = generateWidget(widgetString);
+
+		if (parent == null) {
+			int index = rootWidgets.indexOf(widget);
+			if (index >= 0) {
+				widget = rootWidgets.get(index);
+				widget.titles.add(widgetInfo[0]);
+				widget.hashCodes.add(widgetInfo[4]);
+			} else {
+				rootWidgets.add(widget);
+			}
+		} else {
+			int index = parent.children.indexOf(widget);
+			if (index >= 0) {
+				widget = parent.children.get(index);
+				widget.titles.add(widgetInfo[0]);
+				widget.hashCodes.add(widgetInfo[4]);
+			} else {
+				parent.children.add(widget);
+			}
+		}
+
+		return widget;
+	}
+
+	/**
+	 * <p>
+	 * Creates a new {@link JFCWidget} from a widget string.
+	 * </p>
+	 * 
+	 * @param widgetString
+	 *            string describing the widget
+	 * @return created {@link JFCWidget}
+	 */
+	private JFCWidget generateWidget(String widgetString) {
+		String[] widgetInfo = widgetString.split("','");
+		JFCWidget widget = new JFCWidget();
+		if (widgetInfo[0].startsWith("'Pos(")) {
+			widget.titles.add("Pos");
+		} else {
+			widget.titles.add(widgetInfo[0]);
+		}
+		widget.widgetClass = widgetInfo[1];
+		widget.text = widgetInfo[2];
+		widget.index = widgetInfo[3];
+		widget.hashCodes.add(widgetInfo[4]);
+		return widget;
+	}
+
+	/**
+	 * <p>
+	 * Tries to find the {@link JFCWidget} that the target string identifies in
+	 * the known GUI hierarchy, by traversing the known widgets starting with
+	 * the {@link #rootWidgets}.
+	 * </p>
+	 * 
+	 * @param target
+	 *            target string whose widget is searched for
+	 * @return respective {@link JFCWidget} instance if it is found; null
+	 *         otherwise
+	 */
+	private JFCWidget find(String target) {
+		JFCWidget widget = null;
+		if (target != null) {
+			String[] targetParts = target.split("\\]\\.\\[");
+			if (targetParts.length == 1) {
+				JFCWidget generatedWidget = generateWidget(target.substring(1,
+						target.length() - 1));
+				int index = rootWidgets.indexOf(generatedWidget);
+				if (index >= 0) {
+					widget = rootWidgets.get(index);
+				} else {
+					return null;
+				}
+			} else {
+				JFCWidget parent = null;
+				for (int i = 0; i < targetParts.length; i++) {
+					if (i == 0) {
+						JFCWidget generatedWidget = generateWidget(targetParts[i]
+								.substring(1));
+						int index = rootWidgets.indexOf(generatedWidget);
+						if (index >= 0) {
+							parent = rootWidgets.get(index);
+						} else {
+							return null;
+						}
+					} else if (i == targetParts.length - 1) {
+						JFCWidget generatedWidget = generateWidget(targetParts[i]
+								.substring(0, targetParts[i].length() - 1));
+						int index = parent.children.indexOf(generatedWidget);
+						if (index >= 0) {
+							widget = parent.children.get(index);
+						} else {
+							return null;
+						}
+					} else {
+						JFCWidget generatedWidget = generateWidget(targetParts[i]);
+						int index = parent.children.indexOf(generatedWidget);
+						if (index >= 0) {
+							parent = parent.children.get(index);
+						} else {
+							return null;
+						}
+					}
+				}
+			}
+		}
+		return widget;
+	}
+
+	/**
+	 * <p>
+	 * Internal class used to store JFCWidgets. The implementation is more like
+	 * a C-style structure, than a actual class.
+	 * </p>
+	 * 
+	 * @author Steffen Herbold
+	 * @version 1.0
+	 */
+	private static class JFCWidget {
+
+		/**
+		 * <p>
+		 * Set of all known title strings of the widget.
+		 * </p>
+		 */
+		Set<String> titles = new LinkedHashSet<String>();
+
+		/**
+		 * <p>
+		 * Set of all known hashCodes of the widget.
+		 * </p>
+		 */
+		Set<String> hashCodes = new LinkedHashSet<String>();
+
+		/**
+		 * <p>
+		 * Class of the widget.
+		 * </p>
+		 */
+		String widgetClass;
+
+		/**
+		 * <p>
+		 * Index of the widget.
+		 * </p>
+		 */
+		String index;
+
+		/**
+		 * <p>
+		 * Text of the widget.
+		 * </p>
+		 */
+		String text;
+
+		/**
+		 * <p>
+		 * Pre-computed hash code of the widget.
+		 * </p>
+		 */
+		int hashCode = 0;
+
+		/**
+		 * <p>
+		 * List of children of the widget.
+		 * </p>
+		 */
+		List<JFCWidget> children = new ArrayList<JFCTargetComparator.JFCWidget>();
+
+		/**
+		 * <p>
+		 * Two widgets are equal, if {@link #widgetClass}, {@link #index}, and
+		 * {@link #text} are equal and the intersection of either the
+		 * {@link #hashCodes}, the {@link #titles}, or both of them is not
+		 * empty.
+		 * </p>
+		 * 
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		@Override
+		public boolean equals(Object obj) {
+			if (obj instanceof JFCWidget) {
+				JFCWidget other = (JFCWidget) obj;
+				boolean titleEqual = CollectionUtils.containsAny(titles,
+						other.titles);
+				boolean hashEqual = CollectionUtils.containsAny(hashCodes,
+						other.hashCodes);
+
+				boolean retVal;
+
+				if (widgetClass.equals("Class")) {
+					retVal = (widgetClass.equals(other.widgetClass)
+							&& text.equals(other.text) && (titleEqual || hashEqual));
+				} else {
+					retVal = (widgetClass.equals(other.widgetClass)
+							&& index.equals(other.index)
+							&& text.equals(other.text) && (titleEqual || hashEqual));
+				}
+				return retVal;
+			}
+			return false;
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.Object#hashCode()
+		 */
+		@Override
+		public int hashCode() {
+			if (hashCode == 0) {
+				int multiplier = 7;
+				hashCode = multiplier * hashCode + widgetClass.hashCode();
+				if (!widgetClass.equals("Class")) {
+					hashCode = multiplier * hashCode + index.hashCode();
+				}
+				hashCode = multiplier * hashCode + text.hashCode();
+			}
+			return hashCode;
+		}
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/log4j/Log4JLogger.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/log4j/Log4JLogger.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/log4j/Log4JLogger.java	(revision 432)
@@ -0,0 +1,92 @@
+package de.ugoe.cs.quest.log4j;
+
+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>
+	 * Constructor. Creates a new Log4JLogger and registers the implemented
+	 * listener with the {@link Console}.
+	 * </p>
+	 */
+	public Log4JLogger() {
+		PropertyConfigurator.configure("misc/log4j.properties");
+		logger = Logger.getLogger("de.ugoe.cs.quest");
+		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) {
+		logger.trace(traceMessage);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * de.ugoe.cs.util.console.listener.IErrorListener#errorMsg(java.lang.String
+	 * )
+	 */
+	@Override
+	public void errorMsg(String errMessage) {
+		logger.error(errMessage);
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/AboutDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/AboutDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/AboutDialog.java	(revision 432)
@@ -0,0 +1,72 @@
+package de.ugoe.cs.quest.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/quest-ui-core/src/de/ugoe/cs/quest/swt/AbstractInsertEventComposite.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/AbstractInsertEventComposite.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/AbstractInsertEventComposite.java	(revision 432)
@@ -0,0 +1,20 @@
+package de.ugoe.cs.quest.swt;
+
+import java.util.SortedSet;
+
+import org.eclipse.swt.widgets.Composite;
+
+import de.ugoe.cs.quest.data.Event;
+
+abstract public class AbstractInsertEventComposite extends Composite {
+
+	protected SortedSet<String> targets;
+	
+	public AbstractInsertEventComposite(Composite parent, int style, SortedSet<String> targets) {
+		super(parent, style);
+		this.targets = targets;
+	}
+	
+	public abstract Event<?> getEvent(); 
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/CommandHistoryDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/CommandHistoryDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/CommandHistoryDialog.java	(revision 432)
@@ -0,0 +1,179 @@
+package de.ugoe.cs.quest.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 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("Created batchfile " + filename);
+						} else {
+							Console.traceln("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/quest-ui-core/src/de/ugoe/cs/quest/swt/ConsoleTabComposite.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/ConsoleTabComposite.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/ConsoleTabComposite.java	(revision 432)
@@ -0,0 +1,99 @@
+package de.ugoe.cs.quest.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/quest-ui-core/src/de/ugoe/cs/quest/swt/DataTabComposite.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/DataTabComposite.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/DataTabComposite.java	(revision 432)
@@ -0,0 +1,113 @@
+package de.ugoe.cs.quest.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.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.CommandExecuter;
+
+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 sequencesName : GlobalDataContainer.getInstance().getAllKeys() ) {
+			dataList.add(sequencesName);
+		}
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/EditSequenceDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/EditSequenceDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/EditSequenceDialog.java	(revision 432)
@@ -0,0 +1,192 @@
+package de.ugoe.cs.quest.swt;
+
+import java.util.SortedSet;
+
+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.quest.data.Event;
+
+public class EditSequenceDialog extends Dialog {
+
+	protected Shell shell;
+	private Table table;
+	private TableColumn tblclmnEventType;
+	private TableColumn tblclmnEventTarget;
+
+	private java.util.List<Event<?>> sequence;
+	private SortedSet<String> targets;
+
+	/**
+	 * Create the dialog.
+	 * 
+	 * @param parent
+	 * @param style
+	 */
+	public EditSequenceDialog(Shell parent, int style, SortedSet<String> targets) {
+		super(parent, style);
+		setText("SWT Dialog");
+		this.targets = targets;
+	}
+
+	/**
+	 * 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);
+
+		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();
+		for (Event<?> event : sequence) {
+			TableItem tableItem = new TableItem(table, SWT.NONE);
+			String target = event.getTarget();
+			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[] { event.getType(), target });
+		}
+		for (int i = 0; i < table.getColumnCount(); i++) {
+			table.getColumn(i).pack();
+		}
+	}
+
+	private void openInsertDialog(int position) {
+		InsertAssertionDialog insertDialog = new InsertAssertionDialog(shell,
+				SWT.NONE, targets);
+		Event<?> event = insertDialog.open();
+		if (event != null) {
+			sequence.add(position, event);
+			updateTableContents();
+		}
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/GenerateSequencesDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/GenerateSequencesDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/GenerateSequencesDialog.java	(revision 432)
@@ -0,0 +1,206 @@
+package de.ugoe.cs.quest.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/quest-ui-core/src/de/ugoe/cs/quest/swt/GetObjectNameDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/GetObjectNameDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/GetObjectNameDialog.java	(revision 432)
@@ -0,0 +1,96 @@
+package de.ugoe.cs.quest.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/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertAssertionDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertAssertionDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertAssertionDialog.java	(revision 432)
@@ -0,0 +1,106 @@
+package de.ugoe.cs.quest.swt;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedSet;
+
+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.quest.data.Event;
+
+public class InsertAssertionDialog extends Dialog {
+
+	protected Event<?> result;
+	protected Shell shell;
+	
+	private TabFolder tabFolder;
+	
+	List<AbstractInsertEventComposite> insertEventComposites;
+	SortedSet<String> targets;
+
+	/**
+	 * Create the dialog.
+	 * @param parent
+	 * @param style
+	 */
+	public InsertAssertionDialog(Shell parent, int style, SortedSet<String> targets) {
+		super(parent, style);
+		setText("SWT Dialog");
+		this.targets = targets;
+	}
+
+	/**
+	 * 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, targets);
+		tbtmTextEquals.setControl(compTextEquals);
+		insertEventComposites.add(compTextEquals);
+		
+		TabItem tbtmFileEquals = new TabItem(tabFolder, SWT.NONE);
+		tbtmFileEquals.setText("FileEquals");
+		AbstractInsertEventComposite compFileEquals = new InsertFileEquals(tabFolder, SWT.NO_BACKGROUND, targets);
+		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/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertFileEquals.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertFileEquals.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertFileEquals.java	(revision 432)
@@ -0,0 +1,83 @@
+package de.ugoe.cs.quest.swt;
+
+import java.util.SortedSet;
+
+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.quest.assertions.AssertEvent;
+import de.ugoe.cs.quest.assertions.FileEqualsReplay;
+import de.ugoe.cs.quest.data.Event;
+
+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, SortedSet<String> targets) {
+		super(parent, style, targets);
+		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());
+		AssertEvent<FileEqualsReplay> event = new AssertEvent<FileEqualsReplay>("FileEqualsAssertion");
+		event.addReplayEvent(replay);
+		return event;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertTextEquals.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertTextEquals.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/InsertTextEquals.java	(revision 432)
@@ -0,0 +1,156 @@
+package de.ugoe.cs.quest.swt;
+
+import java.util.SortedSet;
+
+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.quest.assertions.AssertEvent;
+import de.ugoe.cs.quest.assertions.TextEqualsReplay;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.util.ArrayTools;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+public class InsertTextEquals extends AbstractInsertEventComposite {
+	private Text expectedText;
+	private Tree targetTree;
+	private Text targetText;
+	
+	/**
+	 * Create the composite.
+	 * @param parent
+	 * @param style
+	 */
+	public InsertTextEquals(Composite parent, int style, SortedSet<String> targets) {
+		super(parent, style, targets);
+		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));
+		
+		Label lblTargetWidget = new Label(this, SWT.NONE);
+		lblTargetWidget.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+		lblTargetWidget.setText("Target Widget:");
+		
+		targetText = new Text(this, SWT.BORDER);
+		targetText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+		new Label(this, SWT.NONE);
+		
+		targetTree = new Tree(this, SWT.BORDER);
+		targetTree.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				TreeItem[] selection = targetTree.getSelection();
+				if( selection.length==1 ) {
+					TreeItem item = selection[0];
+					String targetString = item.getText();
+					item = item.getParentItem();
+					while( item!=null ) {
+						// TODO the "." is hard coded for the JFCMonitor. should be flexible 
+						targetString = item.getText()+"."+targetString;
+						item = item.getParentItem();
+					}
+					targetText.setText(targetString);
+				}
+			}
+		});
+		targetTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+		buildTargetTree();
+		new Label(this, SWT.NONE);
+		
+		Button btnExpandAll = new Button(this, SWT.NONE);
+		btnExpandAll.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				expandAll(targetTree, true);
+			}
+		});
+		btnExpandAll.setText("Expand all");
+		
+		Button btnCollapseAll = new Button(this, SWT.NONE);
+		btnCollapseAll.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				expandAll(targetTree, false);
+			}
+		});
+		btnCollapseAll.setText("Collapse all");
+
+	}
+
+	@Override
+	protected void checkSubclass() {
+		// Disable the check that prevents subclassing of SWT components
+	}
+
+	@Override
+	public Event<?> getEvent() {		
+		String target = targetText.getText();
+		TextEqualsReplay replay = new TextEqualsReplay(expectedText.getText(), target);
+		AssertEvent<TextEqualsReplay> event = new AssertEvent<TextEqualsReplay>("TextEqualsAssertion");
+		event.setTarget(target);
+		event.addReplayEvent(replay);
+		return event;
+	}
+	
+	private void buildTargetTree() {
+		for( String target : targets ) {
+			TreeItem currentParent = null;
+			TreeItem[] currentItems = targetTree.getItems();
+			
+			//TODO needs rule for target splitting. currently its hard coded for JFCEvent targets.
+			String[] targetParts = target.split("\\.\\[");
+			for( String targetPart : targetParts) {
+				String[] currentTexts = new String[currentItems.length];
+				for( int i=0; i<currentItems.length ; i++ ) {
+					currentTexts[i] = currentItems[i].getText();
+				}
+				if( currentParent!=null ) {
+					targetPart = "["+targetPart;
+				}
+				int index = ArrayTools.findIndex(currentTexts, targetPart);
+				if( index>= 0 ) {
+					currentParent = currentItems[index];
+				} else {
+					if( currentParent==null ) {
+						currentParent = new TreeItem(targetTree, SWT.NULL);
+						currentParent.setText(targetPart);
+					} else {
+						currentParent = new TreeItem(currentParent, SWT.NULL);
+						currentParent.setText(targetPart);
+					}
+				}
+				currentItems = currentParent.getItems();
+			}
+		}
+
+	}
+	
+	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/quest-ui-core/src/de/ugoe/cs/quest/swt/MainWindow.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/MainWindow.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/MainWindow.java	(revision 432)
@@ -0,0 +1,237 @@
+package de.ugoe.cs.quest.swt;
+
+import java.util.List;
+
+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;
+
+	protected Shell shlEventbenchConsole;
+
+	protected TabItem consoleTab;
+	protected TabItem sequencesTab;
+	protected TabItem modelsTab;
+	protected TabItem dataTab;
+	protected ConsoleTabComposite consoleTabComposite;
+	protected SequencesTabComposite sequencesTabComposite;
+	protected ModelsTabComposite modelsTabComposite;
+	protected DataTabComposite dataTabComposite;
+
+	protected CommandHistoryDialog historyDialog;
+
+	public MainWindow(List<String> startupCommands) {
+		this.startupCommands = startupCommands;
+	}
+	
+	/**
+	 * <p>
+	 * Open the window.
+	 * </p>
+	 * 
+	 * @wbp.parser.entryPoint
+	 */
+	public void open() {
+		Display display = Display.getDefault();
+		createContents();
+		new SWTConsole(consoleTabComposite.textConsoleOutput);
+		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 == 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);
+
+		dataTab = new TabItem(tabFolder, SWT.NONE);
+		dataTab.setText("Data");
+
+		dataTabComposite = new DataTabComposite(tabFolder, SWT.NO_BACKGROUND);
+		dataTab.setControl(dataTabComposite);
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/ModelPropertiesDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/ModelPropertiesDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/ModelPropertiesDialog.java	(revision 432)
@@ -0,0 +1,138 @@
+package de.ugoe.cs.quest.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.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.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/quest-ui-core/src/de/ugoe/cs/quest/swt/ModelsTabComposite.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/ModelsTabComposite.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/ModelsTabComposite.java	(revision 432)
@@ -0,0 +1,144 @@
+package de.ugoe.cs.quest.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.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.models.FirstOrderMarkovModel;
+import de.ugoe.cs.quest.models.IDotCompatible;
+import de.ugoe.cs.quest.models.IStochasticProcess;
+import de.ugoe.cs.util.console.CommandExecuter;
+
+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 sequencesName : GlobalDataContainer.getInstance().getAllModelNames() ) {
+			modelList.add(sequencesName);
+		}
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SWTConsole.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SWTConsole.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SWTConsole.java	(revision 432)
@@ -0,0 +1,54 @@
+package de.ugoe.cs.quest.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+
+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.IOutputListener;
+import de.ugoe.cs.util.console.listener.ITraceListener;
+
+public class SWTConsole implements IOutputListener, IErrorListener, ITraceListener, ICommandListener {
+
+	StyledText output;
+	
+	public SWTConsole(StyledText styledText) {
+		Console.getInstance().registerOutputListener(this);
+		Console.getInstance().registerErrorListener(this);
+		Console.getInstance().registerTraceListener(this);
+		Console.getInstance().registerCommandListener(this);
+		this.output = styledText;
+	}
+	
+	@Override
+	public void outputMsg(String newMessage) {
+		output.append(newMessage);
+	}
+
+	@Override
+	public void errorMsg(String errMessage) {
+		appendColored(errMessage, SWT.COLOR_RED);
+	}
+
+	@Override
+	public void traceMsg(String traceMessage) {
+		appendColored(traceMessage, SWT.COLOR_BLUE);
+	}
+	
+	@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);
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SWTHelpers.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SWTHelpers.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SWTHelpers.java	(revision 432)
@@ -0,0 +1,32 @@
+package de.ugoe.cs.quest.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/quest-ui-core/src/de/ugoe/cs/quest/swt/SequencesDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SequencesDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SequencesDialog.java	(revision 432)
@@ -0,0 +1,154 @@
+package de.ugoe.cs.quest.swt;
+
+import java.util.Collection;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+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.quest.SequenceInstanceOf;
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.data.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 SortedSet<String> targets;
+	
+	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, targets);
+					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");
+			targets = null;
+			if( targetObject instanceof SortedSet ) {
+				if( !((SortedSet<?>) targetObject).isEmpty() ) {
+					if( ((SortedSet<?>) targetObject).first() instanceof String ) {
+						targets = (SortedSet<String>) targetObject;
+					}
+				}
+			}
+			if( targets==null ) {
+				targets = new TreeSet<String>();
+				for( java.util.List<Event<?>> sequence : sequences ) {
+					for( Event<?> event : sequence ) {
+						String target = event.getTarget();
+						if( target!=null ) {
+							targets.add(target);
+						}
+					}
+				}
+			}
+		} 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/quest-ui-core/src/de/ugoe/cs/quest/swt/SequencesTabComposite.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SequencesTabComposite.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/SequencesTabComposite.java	(revision 432)
@@ -0,0 +1,137 @@
+package de.ugoe.cs.quest.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.quest.data.GlobalDataContainer;
+import de.ugoe.cs.util.console.CommandExecuter;
+
+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 sequencesName : GlobalDataContainer.getInstance().getAllSequencesNames() ) {
+			sequenceList.add(sequencesName);
+		}
+	}
+	
+	@Override
+	protected void checkSubclass() {
+		// Disable the check that prevents subclassing of SWT components
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/TrainModelDialog.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/TrainModelDialog.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/swt/TrainModelDialog.java	(revision 432)
@@ -0,0 +1,230 @@
+package de.ugoe.cs.quest.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/quest-ui-core/src/de/ugoe/cs/quest/web/WeblogParser.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/WeblogParser.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/WeblogParser.java	(revision 432)
@@ -0,0 +1,497 @@
+package de.ugoe.cs.quest.web;
+
+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 de.ugoe.cs.quest.web.data.WebEvent;
+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<WebEvent>> 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<WebEvent>>> 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<WebEvent>> 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<WebEvent>>> 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>>();
+		int lastId = -1;
+
+		SimpleDateFormat dateFormat = new SimpleDateFormat(
+				"yyyy-MM-dd HH:mm:ss");
+		loadRobotRegex();
+
+		sequences = new ArrayList<List<WebEvent>>();
+		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);
+					
+					WebEvent event = new WebEvent(url, path, timestamp,
+							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<WebEvent>());
+						users.add(cookie);
+					}
+					Integer lastSessionIndex = sessionIds
+							.get(sessionIds.size() - 1);
+					List<WebEvent> lastSession = sequences
+							.get(lastSessionIndex);
+					long lastEventTime = timestamp;
+					if (!lastSession.isEmpty()) {
+						lastEventTime = lastSession.get(lastSession.size() - 1)
+								.getTimestamp();
+					}
+					if (timestamp - lastEventTime > timeout) {
+						sessionIds.add(++lastId);
+						List<WebEvent> newSession = new LinkedList<WebEvent>();
+						newSession.add(event);
+						sequences.add(newSession);
+						users.add(cookie);
+					} else {
+						lastSession.add(event);
+					}
+				} catch (URISyntaxException e) {
+					Console.traceln("Ignored line " + lineCounter + ": "
+							+ e.getMessage());
+				}
+			}
+		}
+		Console.traceln("" + sequences.size() + " user sequences found");
+		pruneSequences();
+		Console.traceln("" + sequences.size()
+				+ " remaining after pruning of sequences shorter than "
+				+ minLength);
+		Set<String> uniqueUsers = new HashSet<String>(users);
+		Console.traceln("" + 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<WebEvent>>>();
+		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<WebEvent>> sequencesUser = new ArrayList<List<WebEvent>>();
+				for (int i = 0; i < sequences.size(); i++) {
+					if (users.get(i).equals(user)) {
+						sequencesUser.add(sequences.get(i));
+					}
+				}
+				sequencesFrequentUsers.add(sequencesUser);
+
+			}
+		}
+		Console.traceln("" + 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/quest-ui-core/src/de/ugoe/cs/quest/web/commands/CMDloadWebSequences.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/commands/CMDloadWebSequences.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/commands/CMDloadWebSequences.java	(revision 432)
@@ -0,0 +1,117 @@
+package de.ugoe.cs.quest.web.commands;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.InvalidParameterException;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.List;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.web.WeblogParser;
+import de.ugoe.cs.quest.web.data.WebEvent;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 InvalidParameterException();
+		}
+		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 InvalidParameterException();
+		}
+
+		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<WebEvent>>> 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 void help() {
+		Console.println("Usage: loadWebSequences <filename> <sequencesName> {<serverUrl>} {<timeout> <minSessionLength> <maxSessionLength>} {<generateFrequentUsers> <frequentUserThreshold>}");
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/data/WebEvent.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/data/WebEvent.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/data/WebEvent.java	(revision 432)
@@ -0,0 +1,108 @@
+package de.ugoe.cs.quest.web.data;
+
+import java.util.List;
+
+import de.ugoe.cs.quest.data.ReplayableEvent;
+
+/**
+ * <p>
+ * This class defines web events (of PHP-based web applications).
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ * 
+ */
+public class WebEvent extends ReplayableEvent<WebRequest> {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * Timestamp of the event.
+	 * </p>
+	 */
+	private final long timestamp;
+
+	/**
+	 * <p>
+	 * Helper method that generates the type of the event based on the of the
+	 * URI, the POST variables, and the 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
+	 * @return type of the event
+	 */
+	private final static String makeType(String path, List<String> postVars,
+			List<String> getVars) {
+		String type = path;
+		if (getVars != null && !getVars.isEmpty()) {
+			type += "+GET" + getVars.toString().replace(" ", "");
+		}
+		if (postVars != null && !postVars.isEmpty()) {
+			type += "+POST" + postVars.toString().replace(" ", "");
+		}
+		return type;
+	}
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new WebEvent.
+	 * </p>
+	 * 
+	 * @param url
+	 *            URL of the server that received the event
+	 * @param path
+	 *            path of the URI
+	 * @param timestamp
+	 *            timestamp of when the event took place
+	 * @param postVars
+	 *            POST variables send with the event
+	 * @param getVars
+	 *            GET variables send with the event
+	 */
+	public WebEvent(String url, String path, long timestamp,
+			List<String> postVars, List<String> getVars) {
+		super(makeType(path, postVars, getVars));
+		this.timestamp = timestamp;
+		super.setTarget(path);
+		addReplayEvent(new WebRequest(url, path, postVars, getVars));
+	}
+
+	/**
+	 * <p>
+	 * Returns the timestamp of the event.
+	 * </p>
+	 * 
+	 * @return timestamp of th event
+	 */
+	public long getTimestamp() {
+		return timestamp;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.ReplayableEvent#equals(java.lang.Object)
+	 */
+	@Override
+	public boolean equals(Object other) {
+		return super.equals(other);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.ReplayableEvent#hashCode()
+	 */
+	@Override
+	public int hashCode() {
+		return super.hashCode();
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/data/WebRequest.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/data/WebRequest.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/web/data/WebRequest.java	(revision 432)
@@ -0,0 +1,172 @@
+package de.ugoe.cs.quest.web.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.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.quest.data.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();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.IReplayable#getTarget()
+	 */
+	@Override
+	public String getTarget() {
+		return targetUri;
+	}
+
+	/**
+	 * <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;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/EventGenerator.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/EventGenerator.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/EventGenerator.java	(revision 432)
@@ -0,0 +1,995 @@
+package de.ugoe.cs.quest.windows;
+
+import java.io.IOException;
+import java.security.InvalidParameterException;
+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 org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.Namespace;
+import org.jdom.input.SAXBuilder;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.windows.data.WindowTree;
+import de.ugoe.cs.quest.windows.data.WindowTreeNode;
+import de.ugoe.cs.quest.windows.data.WindowsEvent;
+import de.ugoe.cs.quest.windows.data.WindowsMessage;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * Translates sequences of windows messages into {@link WindowsEvent}s that can
+ * be used by the EventBench core libraries.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class EventGenerator {
+
+	/**
+	 * <p>
+	 * Helper method that fetches the document node of an XML file.
+	 * </p>
+	 * 
+	 * @param filename
+	 *            name of the XML file
+	 * @return the document node
+	 */
+	private static Document getDocument(String filename) {
+		SAXBuilder builder = new SAXBuilder();
+		Document doc = null;
+
+		try {
+			doc = builder.build(filename);
+			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();
+		}
+
+		return doc;
+	}
+
+	/**
+	 * <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 WindowsEvent currentToken;
+
+	/**
+	 * <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>
+	 * Creates a new EventGenerator. Sets "rules/rules.xml" as default file for
+	 * the rules.
+	 * </p>
+	 */
+	public EventGenerator() {
+		rulesFile = "rules/rules.xml";
+	}
+
+	/**
+	 * <p>
+	 * Tries to match the rules to the given sequence to generate an
+	 * {@link WindowsEvent}.
+	 * </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
+	 */
+	@SuppressWarnings("unchecked")
+	public WindowsEvent generateEvent(List<WindowsMessage> sequence) {
+		Document rulesDoc = getDocument(rulesFile);
+		Element rulesRoot = rulesDoc.getRootElement();
+
+		List<Element> ruleElements = rulesRoot.getChildren("rule",
+				rulesNamespace);
+
+		boolean isMatch = false;
+
+		for (int ruleIndex = 0; ruleIndex < ruleElements.size() && !isMatch; ruleIndex++) {
+			Element currentRule = ruleElements.get(ruleIndex);
+			currentRuleName = currentRule.getAttributeValue("name");
+			currentToken = new WindowsEvent(currentRuleName);
+			currentToken.setDecorator(MFCReplayDecorator.getInstance());
+			isMatch = true;
+			messageStorage = new HashMap<String, Object>();
+			sequenceIterator = sequence.listIterator();
+			List<Element> ruleChildrenMsg = currentRule.getChildren("msg",
+					rulesNamespace);
+
+			int i = 0;
+			while (isMatch && i < ruleChildrenMsg.size()) {
+				Element messageElement = ruleChildrenMsg.get(i);
+				if ("true".equals(messageElement.getAttributeValue("multiple"))) {
+					Element nextMessageElement = null;
+					if (i + 1 < ruleChildrenMsg.size()) {
+						nextMessageElement = ruleChildrenMsg.get(i + 1);
+					}
+					try {
+						isMatch = matchMultipleMessages(messageElement,
+								nextMessageElement);
+					} catch (InvalidParameterException e) {
+						Console.printerrln(e.getMessage());
+					}
+				} else {
+					try {
+						isMatch = matchSingleMessage(messageElement);
+					} catch (InvalidParameterException e) {
+						Console.printerrln(e.getMessage());
+					}
+				}
+				i++;
+			}
+			if (isMatch) {
+				List<Element> ruleChildren = currentRule.getChildren();
+				for (Element genMsgElement : ruleChildren) {
+					if (genMsgElement.getName().equals("genMsg")) {
+						try {
+							generateReplayMessage(genMsgElement);
+						} catch (InvalidParameterException e) {
+							Console.printerrln(e.getMessage());
+							currentToken.invalidateReplay();
+						}
+					} else if (genMsgElement.getName().equals("genMsgSeq")) {
+						try {
+							generateReplaySequence(genMsgElement);
+							currentToken.invalidateReplay();
+						} catch (InvalidParameterException e) {
+							Console.printerrln(e.getMessage());
+							currentToken.invalidateReplay();
+						}
+					}
+				}
+				Element idinfoElement = currentRule.getChild("idinfo",
+						rulesNamespace);
+				if (idinfoElement != null) {
+					// cannot be empty if document is valid
+					List<Element> valueElements = idinfoElement.getChildren();
+					currentToken.setIdInfo(getTermValue(null,
+							valueElements.get(0)));
+				}
+				Console.traceln(currentRule.getAttributeValue("name")
+						+ currentToken.getIdInfo() + " matched");
+			} else {
+				currentToken = null;
+			}
+		}
+		if (!isMatch) {
+			Console.traceln("no match found for sequence: "
+					+ sequence.toString());
+		}
+		return currentToken;
+	}
+
+	// ////////////////////////////////////////////////////////////
+	// Helper functions for matching of events, i.e., msg-nodes //
+	// ////////////////////////////////////////////////////////////
+
+	/**
+	 * <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(Element messageElement) {
+		boolean isMatch = false;
+		WindowsMessage currentMessage = null;
+
+		int type = Integer.parseInt(messageElement.getAttributeValue("type"));
+
+		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 = evalEqualRestrictions(currentMessage, messageElement);
+
+				// in case the message is a match, eval storage children
+				if (isMatch) {
+					handleStorage(messageElement, currentMessage);
+					currentToken.setTarget(currentMessage
+							.getXmlWindowDescription());
+					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 matchMultipleMessages(Element messageElement,
+			Element nextMessageElement) {
+		boolean isMatch = false;
+		boolean isCurrentMatch = false;
+		boolean nextMatchFound = false;
+		WindowsMessage currentMessage = null;
+		WindowsMessage nextMessage = null;
+
+		int type = Integer.parseInt(messageElement.getAttributeValue("type"));
+
+		int nextType = -1;
+		if (nextMessageElement != null) {
+			nextType = Integer.parseInt(nextMessageElement
+					.getAttributeValue("type"));
+		}
+
+		while (!nextMatchFound && sequenceIterator.hasNext()) {
+			currentMessage = sequenceIterator.next();
+			if (type == currentMessage.getType()) {
+				isCurrentMatch = evalEqualRestrictions(currentMessage,
+						messageElement);
+				isMatch = isMatch || isCurrentMatch;
+
+				if (isCurrentMatch) {
+					handleStorage(messageElement, currentMessage);
+					currentToken.setTarget(currentMessage
+							.getXmlWindowDescription());
+					currentToken
+							.setTargetShort(currentMessage.getParentNames());
+				}
+			}
+			if (nextMessageElement != 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 = evalEqualRestrictions(nextMessage,
+							nextMessageElement);
+				}
+
+			}
+		}
+
+		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
+	 */
+	@SuppressWarnings("unchecked")
+	private boolean evalEqualRestrictions(WindowsMessage currentMessage,
+			Element messageElement) {
+		boolean isMatch = true;
+		for (Element childElement : (List<Element>) messageElement.getChildren(
+				"equals", rulesNamespace)) {
+			List<Element> termElements = childElement.getChildren();
+			// the size 2 of termElements is guaranteed by the XML schema
+			String value1 = getTermValue(currentMessage, termElements.get(0));
+			String value2 = getTermValue(currentMessage, termElements.get(1));
+			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(Element messageElement,
+			WindowsMessage currentMessage) {
+		for (Element childElement : (List<Element>) messageElement.getChildren(
+				"store", rulesNamespace)) {
+			String identifier = childElement.getAttributeValue("var");
+			messageStorage.put(identifier, currentMessage);
+			resolveHwnd(currentMessage, childElement);
+		}
+		for (Element childElement : (List<Element>) messageElement.getChildren(
+				"storeSeq", rulesNamespace)) {
+			String identifier = childElement.getAttributeValue("varSeq");
+			Object tmp = messageStorage.get(identifier);
+			List<WindowsMessage> storedSequence;
+			if (tmp == null || tmp instanceof WindowsMessage) {
+				storedSequence = new LinkedList<WindowsMessage>();
+				storedSequence.add(currentMessage);
+				messageStorage.put(identifier, storedSequence);
+			} else if (tmp instanceof List<?>) {
+				storedSequence = (List<WindowsMessage>) tmp;
+				storedSequence.add(currentMessage);
+				messageStorage.put(identifier, storedSequence);
+			}
+			resolveHwnd(currentMessage, childElement);
+		}
+	}
+
+	/**
+	 * <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 childElement
+	 *            child element of the store node that represents the resolve
+	 */
+	@SuppressWarnings("unchecked")
+	private void resolveHwnd(WindowsMessage currentMessage, Element childElement) {
+		List<Element> resolveElements = childElement.getChildren("resolveHwnd",
+				rulesNamespace);
+		for (Element resolveElement : resolveElements) {
+			String param = resolveElement.getAttributeValue("param");
+			String storeParam = resolveElement.getAttributeValue("storeParam");
+			int paramHwnd = Integer
+					.parseInt(currentMessage.getParameter(param));
+			WindowTreeNode node = WindowTree.getInstance().find(paramHwnd);
+			if (node != null) {
+				currentMessage.addParameter(storeParam,
+						node.xmlRepresentation());
+			}
+		}
+	}
+
+	// /////////////////////////////////////////////////////
+	// 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
+	 */
+	@SuppressWarnings("unchecked")
+	private void generateReplayMessage(Element genMsgElement) {
+		List<Element> genMsgChildren = genMsgElement.getChildren();
+		WindowsMessage generatedMessage = null;
+		if (genMsgChildren.size() == 1) { // replay stored message without
+											// change
+			String obj = genMsgChildren.get(0).getAttributeValue("obj");
+			generatedMessage = getStoredMessageVariable(null, obj);
+		} else { // generate message according to the rule
+			for (Element genMsgChild : genMsgChildren) {
+				Element termElement = (Element) genMsgChild.getChildren()
+						.get(0);
+				if (genMsgChild.getName().equals("type")) {
+					try {
+						int msgType = Integer.parseInt(getTermValue(null,
+								termElement));
+						generatedMessage = new WindowsMessage(msgType);
+					} catch (NumberFormatException e) {
+						throw new InvalidParameterException(
+								"Failure generating replay sequence for rule "
+										+ currentRuleName
+										+ ": Defined type is not an integer.");
+					}
+				} else if (genMsgChild.getName().equals("target")) {
+					String targetString = getTermValue(null, termElement);
+					generatedMessage.setXmlWindowDescription(targetString);
+				} else if (genMsgChild.getName().equals("LPARAM")) {
+					String paramValueStr = getTermValue(null, termElement);
+					long paramValue = 0;
+					Element loword = genMsgChild.getChild("LOWORD",
+							rulesNamespace);
+					if (loword != null) {
+						paramValue = loHiWord(genMsgChild);
+						generatedMessage.setLPARAM(paramValue);
+					} else {
+						try {
+							paramValue = Integer.parseInt(paramValueStr);
+							generatedMessage.setLPARAM(paramValue);
+						} catch (NumberFormatException e) {
+							generatedMessage
+									.setLPARAMasWindowDesc(paramValueStr);
+						}
+					}
+				} else if (genMsgChild.getName().equals("WPARAM")) {
+					String paramValueStr = getTermValue(null, termElement);
+					long paramValue = 0;
+					Element loword = genMsgChild.getChild("LOWORD",
+							rulesNamespace);
+					if (loword != null) {
+						paramValue = loHiWord(genMsgChild);
+						generatedMessage.setWPARAM(paramValue);
+					} else {
+						try {
+							paramValue = Integer.parseInt(paramValueStr);
+							generatedMessage.setWPARAM(paramValue);
+						} catch (NumberFormatException e) {
+							generatedMessage
+									.setWPARAMasWindowDesc(paramValueStr);
+						}
+					}
+				}
+			}
+		}
+		if (generatedMessage != null) {
+			int delay = Integer.parseInt(genMsgElement
+					.getAttributeValue("delay"));
+			generatedMessage.setDelay(delay);
+		} else {
+			currentToken.invalidateReplay();
+		}
+		currentToken.addReplayEvent(generatedMessage);
+	}
+
+	/**
+	 * Handles genMsgSeq-nodes and adds the replay to the {@link Event} that is
+	 * generated.</p>
+	 * 
+	 * @param genMsgElement
+	 *            {@link Element} representing the genMsgSeq-node.
+	 */
+	@SuppressWarnings("unchecked")
+	private void generateReplaySequence(Element genMsgElement) {
+		List<Element> genMsgSeqChildren = genMsgElement.getChildren();
+		List<WindowsMessage> generatedMessageSeq = new LinkedList<WindowsMessage>();
+		if (genMsgSeqChildren.size() == 1) {
+			String obj = genMsgSeqChildren.get(0).getAttributeValue("seqObj");
+			generatedMessageSeq = getStoredSeqVariable(obj);
+		} else {
+			boolean msgsGenerated = false;
+			int constMsgType = 0;
+			for (Element genMsgSeqChild : genMsgSeqChildren) {
+				Element termElement = (Element) genMsgSeqChild.getChildren()
+						.get(0);
+				if (genMsgSeqChild.getName().equals("type")) {
+					// note: cannot easily be extracted because of mulitple
+					// return values
+					if (termElement.getName().equals("seqValue")) {
+						String obj = termElement.getAttributeValue("seqObj");
+						List<WindowsMessage> seqVar = getStoredSeqVariable(obj);
+						for (WindowsMessage msg : seqVar) {
+							generatedMessageSeq.add(new WindowsMessage(msg
+									.getType()));
+						}
+						msgsGenerated = true;
+					} else { // constValue type
+						constMsgType = Integer.parseInt(getTermValue(null,
+								termElement));
+					}
+				} else if (genMsgSeqChild.getName().equals("target")) {
+					msgsGenerated = createSequenceTarget(generatedMessageSeq,
+							msgsGenerated, constMsgType, termElement);
+				} else if (genMsgSeqChild.getName().equals("LPARAM")) {
+					msgsGenerated = createSequenceLParam(generatedMessageSeq,
+							msgsGenerated, constMsgType, termElement);
+				} else if (genMsgSeqChild.getName().equals("WPARAM")) {
+					msgsGenerated = createSequenceWParam(generatedMessageSeq,
+							msgsGenerated, constMsgType, termElement);
+				}
+			}
+		}
+		currentToken.addReplaySequence(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 boolean createSequenceTarget(
+			List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated,
+			int constMsgType, Element termElement)
+			throws NoSuchElementException {
+		Iterator<WindowsMessage> seqIterator = generatedMessageSeq.iterator();
+		if (termElement.getName().equals("seqValue")) {
+			String obj = termElement.getAttributeValue("seqObj");
+			List<WindowsMessage> seqVar = getStoredSeqVariable(obj);
+			if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) {
+				throw new InvalidParameterException(
+						"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) {
+				WindowsMessage currentSeqMsg = getCurrentSeqMsg(
+						generatedMessageSeq, msgsGenerated, constMsgType,
+						seqIterator);
+				String targetString = msg.getParameter(termElement
+						.getAttributeValue("param"));
+				currentSeqMsg.setXmlWindowDescription(targetString);
+			}
+			msgsGenerated = true;
+		} 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.
+			 */
+		}
+		return msgsGenerated;
+	}
+
+	/**
+	 * <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 boolean createSequenceLParam(
+			List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated,
+			int constMsgType, Element termElement)
+			throws NoSuchElementException {
+		Iterator<WindowsMessage> seqIterator = generatedMessageSeq.iterator();
+		if (termElement.getName().equals("seqValue")) {
+			String obj = termElement.getAttributeValue("seqObj");
+			List<WindowsMessage> seqVar = getStoredSeqVariable(obj);
+			if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) {
+				throw new InvalidParameterException(
+						"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) {
+				WindowsMessage currentSeqMsg = getCurrentSeqMsg(
+						generatedMessageSeq, msgsGenerated, constMsgType,
+						seqIterator);
+				String paramValueStr = msg.getParameter(termElement
+						.getAttributeValue("param"));
+				int paramValue = 0;
+				try {
+					paramValue = Integer.parseInt(paramValueStr);
+					currentSeqMsg.setLPARAM(paramValue);
+				} catch (NumberFormatException e) {
+					currentSeqMsg.setLPARAMasWindowDesc(paramValueStr);
+				}
+			}
+			if (seqIterator.hasNext()) {
+				// the first seq-var has a different number of elements than the
+				// current one
+				throw new NoSuchElementException();
+			}
+			msgsGenerated = true;
+		} else { // const value
+			int paramValue = Integer.parseInt(getTermValue(null, termElement));
+			while (seqIterator.hasNext()) {
+				seqIterator.next().setLPARAM(paramValue);
+			}
+		}
+		return msgsGenerated;
+	}
+
+	/**
+	 * <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 boolean createSequenceWParam(
+			List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated,
+			int constMsgType, Element termElement)
+			throws NoSuchElementException {
+		Iterator<WindowsMessage> seqIterator = generatedMessageSeq.iterator();
+		if (termElement.getName().equals("seqValue")) {
+			String obj = termElement.getAttributeValue("seqObj");
+			List<WindowsMessage> seqVar = getStoredSeqVariable(obj);
+			if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) {
+				throw new InvalidParameterException(
+						"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) {
+				WindowsMessage currentSeqMsg = getCurrentSeqMsg(
+						generatedMessageSeq, msgsGenerated, constMsgType,
+						seqIterator);
+				String paramValueStr = msg.getParameter(termElement
+						.getAttributeValue("param"));
+				int paramValue = 0;
+				try {
+					paramValue = Integer.parseInt(paramValueStr);
+					currentSeqMsg.setWPARAM(paramValue);
+				} catch (NumberFormatException e) {
+					currentSeqMsg.setWPARAMasWindowDesc(paramValueStr);
+				}
+			}
+			if (seqIterator.hasNext()) {
+				// the first seq-var has a different number of elements than the
+				// current one
+				throw new NoSuchElementException();
+			}
+			msgsGenerated = true;
+		} else { // const value
+			int paramValue = Integer.parseInt(getTermValue(null, termElement));
+			while (seqIterator.hasNext()) {
+				seqIterator.next().setWPARAM(paramValue);
+			}
+		}
+		return msgsGenerated;
+	}
+
+	/**
+	 * <p>
+	 * If a message sequence is already generated, i.e., msgsGenerated is true,
+	 * the seqIterator is used to iterate through these messages and return the
+	 * current one. If the message sequence is not yet generated, i.e.,
+	 * msgsGenerated is false, the message sequence is generated on the fly
+	 * during each call of this message and the newly generated messages are
+	 * returned.
+	 * </p>
+	 * 
+	 * @param generatedMessageSeq
+	 *            message sequence
+	 * @param msgsGenerated
+	 *            indicates if generatedMessageSeq is already generated or has
+	 *            to be generated on the fly by this method
+	 * @param constMsgType
+	 *            type of the message to be used for message generation
+	 * @param seqIterator
+	 *            iterates through an already generated message sequence; must
+	 *            not be {@code null}, if msgsGenerated is true
+	 * @return current message
+	 */
+	private WindowsMessage getCurrentSeqMsg(
+			List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated,
+			int constMsgType, Iterator<WindowsMessage> seqIterator) {
+		WindowsMessage currentSeqMsg = null;
+		if (msgsGenerated) {
+			currentSeqMsg = seqIterator.next();
+		} else {
+			currentSeqMsg = new WindowsMessage(constMsgType);
+			generatedMessageSeq.add(currentSeqMsg);
+		}
+		return currentSeqMsg;
+	}
+
+	// ////////////////////////////
+	// General helper functions //
+	// ////////////////////////////
+
+	/**
+	 * <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 InvalidParameterException
+	 *             thrown in case of invalid uses of "this" or if no message
+	 *             with the identifier obj is found in the storage
+	 */
+	private WindowsMessage getStoredMessageVariable(
+			WindowsMessage currentMessage, String obj)
+			throws InvalidParameterException {
+		WindowsMessage varMessage = null;
+		if (obj.equals("this")) {
+			if (currentMessage == null) {
+				throw new InvalidParameterException(
+						"Failure obtaining term value for rule "
+								+ currentRuleName
+								+ ": \"this\" is not a valid name for generating runtime messages.");
+			}
+			varMessage = currentMessage;
+		} else {
+			Object tmp = messageStorage.get(obj);
+			if (tmp instanceof WindowsMessage) {
+				varMessage = (WindowsMessage) tmp;
+			} else {
+				throw new InvalidParameterException(
+						"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 InvalidParameterException
+	 *             thrown if no message sequences with the identifier obj is
+	 *             found in the storage
+	 */
+	@SuppressWarnings("unchecked")
+	private List<WindowsMessage> getStoredSeqVariable(String obj)
+			throws InvalidParameterException {
+		List<WindowsMessage> varMsgSeq = null;
+		Object tmp = messageStorage.get(obj);
+		if (tmp instanceof List<?>) {
+			varMsgSeq = (List<WindowsMessage>) tmp;
+		} else {
+			throw new InvalidParameterException(
+					"Failure obtaining term value for rule " + currentRuleName
+							+ ": No sequence \"" + obj + "\" store.");
+		}
+		return varMsgSeq;
+	}
+
+	/**
+	 * <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 String getTermValue(WindowsMessage currentMessage,
+			Element termElement) {
+		String value = null;
+		WindowsMessage varMessage = null;
+		if (termElement.getName().equals("constValue")) {
+			value = termElement.getAttributeValue("value");
+		} else if (termElement.getName().equals("paramValue")) {
+			String objectName = termElement.getAttributeValue("obj");
+			varMessage = getStoredMessageVariable(currentMessage, objectName);
+			if (varMessage != null) {
+				String param = termElement.getAttributeValue("param");
+				value = varMessage.getParameter(param);
+			}
+		} else if (termElement.getName().equals("winInfoValue")) {
+			String objectName = termElement.getAttributeValue("obj");
+			varMessage = getStoredMessageVariable(currentMessage, objectName);
+			if (varMessage != null) {
+				String paramString = termElement.getAttributeValue("winParam");
+				if (paramString.equals("class")) {
+					value = varMessage.getWindowClass();
+				} else if (paramString.equals("resourceId")) {
+					value = "" + varMessage.getWindowResourceId();
+				} else if (paramString.equals("hwnd")) {
+					value = "" + varMessage.getHwnd();
+				} else if (paramString.equals("parentTarget")) {
+					String target = varMessage.getXmlWindowDescription();
+					int index = target.lastIndexOf("<");
+					if (index == 0) {
+						Console.traceln("Trying to adress parent of top-level window! Replay probably invalid!");
+					}
+					value = target.substring(0, index);
+				} else if (paramString.equals("parentClass")) {
+					value = varMessage.getParentClass();
+				}
+			}
+		} else if (termElement.getName().equals("msgInfoValue")) {
+			String objectName = termElement.getAttributeValue("obj");
+			varMessage = getStoredMessageVariable(currentMessage, objectName);
+			if (varMessage != null) {
+				String paramString = termElement.getAttributeValue("msgParam");
+				if (paramString.equals("type")) {
+					value = "" + varMessage.getType();
+				} else if (paramString.equals("target")) {
+					value = varMessage.getXmlWindowDescription();
+				}
+			}
+		}
+		return value;
+	}
+
+	/**
+	 * <p>
+	 * Handles term-nodes contained by equalSeq nodes.
+	 * </p>
+	 * 
+	 * @param termElement
+	 *            {@link Element} representing the term-node
+	 * @return list of values of the term
+	 */
+	private List<String> getTermValueSeq(Element termElement) {
+		List<String> values = new LinkedList<String>();
+		if (termElement.getName().equals("seqValue")) {
+			String obj = termElement.getAttributeValue("seqObj");
+			String param = termElement.getAttributeValue("param");
+			List<WindowsMessage> seqVar = getStoredSeqVariable(obj);
+
+			for (WindowsMessage msg : seqVar) {
+				// msg.getParameter returns null, if parameter is not found,
+				// therefore the List can contain null-values
+				values.add(msg.getParameter(param));
+			}
+		}
+		return values;
+	}
+
+	/**
+	 * <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(Element param) {
+		Element loword = param.getChild("LOWORD", rulesNamespace);
+		Element hiword = param.getChild("HIWORD", rulesNamespace);
+		String lowordStr = getTermValue(null, (Element) loword.getChildren()
+				.get(0));
+		String hiwordStr = getTermValue(null, (Element) hiword.getChildren()
+				.get(0));
+		return MAKEPARAM(Short.parseShort(lowordStr),
+				Short.parseShort(hiwordStr));
+	}
+
+	/**
+	 * <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;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerCreate.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerCreate.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerCreate.java	(revision 432)
@@ -0,0 +1,125 @@
+package de.ugoe.cs.quest.windows;
+
+import de.ugoe.cs.quest.windows.data.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>
+	 */
+	public HandlerCreate() {
+		super();
+	}
+
+	/**
+	 * <p>
+	 * Name of the created window.
+	 * </p>
+	 */
+	private String windowName;
+
+	/**
+	 * <p>
+	 * HWND of the created window.
+	 * </p>
+	 */
+	private int hwnd;
+
+	/**
+	 * <p>
+	 * HWND of the created window's parent.
+	 * </p>
+	 */
+	private int 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.quest.windows.MessageHandler#onEndElement()
+	 */
+	@Override
+	public void onEndElement() {
+		if (hwnd != 0) {
+			WindowTree.getInstance().add(parentHwnd, hwnd, windowName,
+					resourceId, className, isModal);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * de.ugoe.cs.quest.windows.MessageHandler#onParameter(java.lang.String
+	 * , java.lang.String)
+	 */
+	@Override
+	public void onParameter(String name, String value) {
+		if (name.equals("window.hwnd")) {
+			hwnd = Integer.parseInt(value);
+		} else if (name.equals("window.name")) {
+			windowName = value;
+		} else if (name.equals("window.parent.hwnd")) {
+			parentHwnd = Integer.parseInt(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.quest.windows.MessageHandler#onStartElement()
+	 */
+	@Override
+	public void onStartElement() {
+		windowName = "";
+		hwnd = 0;
+		parentHwnd = 0;
+		resourceId = 0;
+		className = "";
+		isModal = false;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerDestroy.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerDestroy.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerDestroy.java	(revision 432)
@@ -0,0 +1,68 @@
+package de.ugoe.cs.quest.windows;
+
+import de.ugoe.cs.quest.windows.data.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>
+	 */
+	public HandlerDestroy() {
+		super();
+	}
+
+	/**
+	 * <p>
+	 * HWND of the window that is destroyed.
+	 * </p>
+	 */
+	private int hwnd;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.windows.MessageHandler#onEndElement()
+	 */
+	@Override
+	public void onEndElement() {
+		if (hwnd != 0) {
+			WindowTree.getInstance().remove(hwnd);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * de.ugoe.cs.quest.windows.MessageHandler#onParameter(java.lang.String
+	 * , java.lang.String)
+	 */
+	@Override
+	public void onParameter(String name, String value) {
+		if (name.equals("window.hwnd")) {
+			hwnd = Integer.parseInt(value);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.windows.MessageHandler#onStartElement()
+	 */
+	@Override
+	public void onStartElement() {
+		hwnd = 0;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerSetText.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerSetText.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/HandlerSetText.java	(revision 432)
@@ -0,0 +1,78 @@
+package de.ugoe.cs.quest.windows;
+
+import de.ugoe.cs.quest.windows.data.WindowTree;
+import de.ugoe.cs.quest.windows.data.WindowTreeNode;
+
+/**
+ * <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>
+	 */
+	public HandlerSetText() {
+		super();
+	}
+
+	/**
+	 * <p>
+	 * New name of the window.
+	 * </p>
+	 */
+	private String windowName;
+
+	/**
+	 * <p>
+	 * HWND of the window.
+	 * </p>
+	 */
+	private int hwnd;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.windows.MessageHandler#onEndElement()
+	 */
+	@Override
+	public void onEndElement() {
+		if (hwnd != 0) {
+			WindowTreeNode node = WindowTree.getInstance().find(hwnd);
+			node.setName(windowName);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * de.ugoe.cs.quest.windows.MessageHandler#onParameter(java.lang.String
+	 * , java.lang.String)
+	 */
+	@Override
+	public void onParameter(String name, String value) {
+		if (name.equals("window.hwnd")) {
+			hwnd = Integer.parseInt(value);
+		} else if (name.equals("window.newText")) {
+			windowName = value;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.windows.MessageHandler#onStartElement()
+	 */
+	@Override
+	public void onStartElement() {
+		windowName = "";
+		hwnd = 0;
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/LogPreprocessor.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/LogPreprocessor.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/LogPreprocessor.java	(revision 432)
@@ -0,0 +1,219 @@
+package de.ugoe.cs.quest.windows;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+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()) {
+			throw new IOException(path + " is not a directory");
+		}
+		String absolutPath = folder.getAbsolutePath();
+		for (String filename : folder.list()) {
+			String source = absolutPath + "/" + filename;
+			Console.traceln("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/quest-ui-core/src/de/ugoe/cs/quest/windows/MFCLogParser.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MFCLogParser.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MFCLogParser.java	(revision 432)
@@ -0,0 +1,277 @@
+package de.ugoe.cs.quest.windows;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+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.quest.windows.data.WindowTree;
+import de.ugoe.cs.quest.windows.data.WindowsEvent;
+import de.ugoe.cs.quest.windows.data.WindowsMessage;
+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>
+	 * Handle to the message that is currently parsed.
+	 * </p>
+	 */
+	private WindowsMessage currentMessage;
+
+	/**
+	 * <p>
+	 * {@link SequenceSplitter} instance used by the {@link MFCLogParser}.
+	 * </p>
+	 */
+	private SequenceSplitter sequenceSplitter;
+
+	/**
+	 * <p>
+	 * Collection of event sequences that is contained in the log file, which is
+	 * parsed.
+	 * </p>
+	 */
+	private Collection<List<WindowsEvent>> 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<Integer, 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) {
+		sequenceSplitter = new SequenceSplitter();
+		sequences = new LinkedList<List<WindowsEvent>>();
+		currentHandler = null;
+		this.countMessageOccurences = countMessageOccurences;
+		if (countMessageOccurences) {
+			typeCounter = new TreeMap<Integer, Integer>();
+		}
+
+	}
+
+	/**
+	 * <p>
+	 * Returns the collection of event sequences that is obtained from parsing
+	 * log files.
+	 * </p>
+	 * 
+	 * @return collection of event sequences
+	 */
+	public Collection<List<WindowsEvent>> getSequences() {
+		return sequences;
+	}
+
+	/*
+	 * (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("start of session");
+			sequenceSplitter = new SequenceSplitter();
+		} else if (qName.equals("msg")) {
+			String msgType = atts.getValue("type");
+			int msgInt = -1;
+			try {
+				msgInt = Integer.parseInt(msgType);
+
+				if (countMessageOccurences) {
+					Integer currentCount = typeCounter.get(msgInt);
+					if (currentCount == null) {
+						typeCounter.put(msgInt, 1);
+					} else {
+						typeCounter.put(msgInt, currentCount + 1);
+					}
+				}
+
+				if (msgInt == MessageDefs.WM_CREATE) {
+					currentHandler = new HandlerCreate();
+					currentHandler.onStartElement();
+				} else if (msgInt == MessageDefs.WM_DESTROY) {
+					currentHandler = new HandlerDestroy();
+					currentHandler.onStartElement();
+				} else if (msgInt == MessageDefs.WM_SETTEXT) {
+					currentHandler = new HandlerSetText();
+					currentHandler.onStartElement();
+				} else {
+					currentMessage = new WindowsMessage(msgInt);
+				}
+			} catch (NumberFormatException e) {
+				Console.printerrln("Invalid message type: type not a number");
+				e.printStackTrace();
+			}
+		} else if (qName.equals("param")) {
+			if (currentHandler != null) {
+				currentHandler.onParameter(atts.getValue("name"),
+						atts.getValue("value"));
+			} else {
+				currentMessage.addParameter(atts.getValue("name"),
+						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 {
+					currentMessage.setTarget(WindowTree.getInstance());
+					sequenceSplitter.addMessage(currentMessage);
+				} catch (InvalidParameterException e) {
+					Console.traceln(e.getMessage() + " WindowsMessage "
+							+ currentMessage + " ignored.");
+				}
+			}
+		} else if (qName.equals("session")) {
+			sequenceSplitter.endSession();
+			List<WindowsEvent> seq = sequenceSplitter.getSequence();
+			if( seq!=null && !seq.isEmpty() ) {
+				sequences.add(seq);
+			}
+			Console.traceln("end of session");
+		}
+	}
+
+	/**
+	 * <p>
+	 * Parses a given log file created by the MFCMonitor and adds its contents
+	 * to the collection of event sequences.
+	 * </p>
+	 * 
+	 * @param filename
+	 *            name and path of the log file
+	 */
+	public void parseFile(String filename) {
+		if (filename == null) {
+			throw new InvalidParameterException("filename 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(filename)));//, "UTF-8"));
+		//} catch (UnsupportedEncodingException e) {
+		//	e.printStackTrace();
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		} catch (SAXException e) {
+			e.printStackTrace();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+		if (inputSource != null) {
+			inputSource.setSystemId("file://"
+					+ new File(filename).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()
+						+ ".");
+				e.printStackTrace();
+			} catch (SAXException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		if (countMessageOccurences) {
+			Console.println("Message statistics:");
+			Console.println(typeCounter.toString()
+					.replace(" ", StringTools.ENDLINE)
+					.replaceAll("[\\{\\}]", ""));
+		}
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MFCReplayDecorator.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MFCReplayDecorator.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MFCReplayDecorator.java	(revision 432)
@@ -0,0 +1,96 @@
+package de.ugoe.cs.quest.windows;
+
+import de.ugoe.cs.quest.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.quest.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.quest.IReplayDecorator#getFooter()
+	 */
+	@Override
+	public String getFooter() {
+		return "</log>" + StringTools.ENDLINE;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.IReplayDecorator#getSessionHeader(int)
+	 */
+	@Override
+	public String getSessionHeader(int sessionId) {
+		return " <session id=\"" + sessionId + "\">" + StringTools.ENDLINE;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.IReplayDecorator#getSessionFooter(int)
+	 */
+	@Override
+	public String getSessionFooter(int sessionId) {
+		return " </session>" + StringTools.ENDLINE;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MessageDefs.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MessageDefs.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MessageDefs.java	(revision 432)
@@ -0,0 +1,167 @@
+package de.ugoe.cs.quest.windows;
+
+/**
+ * <p>
+ * Contains definitions of windows message codes, such that they can be used
+ * internally by their name and not their integer value, to improve the
+ * readability of the source code.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public interface MessageDefs {
+
+	public static final int WM_NULL = 0;
+	public static final int WM_CREATE = 1;
+	public static final int WM_DESTROY = 2;
+	public static final int WM_MOVE = 3;
+	public static final int WM_SIZE = 5;
+	public static final int WM_ACTIVATE = 6;
+	public static final int WM_SETFOCUS = 7;
+	public static final int WM_KILLFOCUS = 8;
+	public static final int WM_ENABLE = 10;
+	public static final int WM_SETREDRAW = 11;
+	public static final int WM_SETTEXT = 12;
+	public static final int WM_GETTEXT = 13;
+	public static final int WM_GETTEXTLENGTH = 14;
+	public static final int WM_PAINT = 15;
+	public static final int WM_CLOSE = 16;
+	public static final int WM_QUERYENDSESSION = 17;
+	public static final int WM_QUIT = 18;
+	public static final int WM_QUERYOPEN = 19;
+	public static final int WM_ERASEBKGND = 20;
+	public static final int WM_SYSCOLORCHANGE = 21;
+	public static final int WM_ENDSESSION = 22;
+	public static final int WM_SHOWWINDOW = 24;
+	public static final int WM_CTLCOLOR = 25;
+	public static final int WM_WININICHANGE = 26;
+	public static final int WM_DEVMODECHANGE = 27;
+	public static final int WM_ACTIVATEAPP = 28;
+	public static final int WM_FONTCHANGE = 29;
+	public static final int WM_TIMECHANGE = 30;
+	public static final int WM_CANCELMODE = 31;
+	public static final int WM_SETCURSOR = 32;
+	public static final int WM_MOUSEACTIVATE = 33;
+	public static final int WM_CHILDACTIVATE = 34;
+	public static final int WM_QUEUESYNC = 35;
+	public static final int WM_GETMINMAXINFO = 36;
+	public static final int WM_PAINTICON = 38;
+	public static final int WM_ICONERASEBKGND = 39;
+	public static final int WM_NEXTDLGCTL = 40;
+	public static final int WM_SPOOLERSTATUS = 42;
+	public static final int WM_DRAWITEM = 43;
+	public static final int WM_MEASUREITEM = 44;
+	public static final int WM_DELETEITEM = 45;
+	public static final int WM_VKEYTOITEM = 46;
+	public static final int WM_CHARTOITEM = 47;
+	public static final int WM_SETFONT = 48;
+	public static final int WM_GETFONT = 49;
+	public static final int WM_SETHOTKEY = 50;
+	public static final int WM_GETHOTKEY = 51;
+	public static final int WM_QUERYDRAGICON = 55;
+	public static final int WM_COMPAREITEM = 57;
+	public static final int WM_GETOBJECT = 61;
+	public static final int WM_COMPACTING = 65;
+	public static final int WM_COMMNOTIFY = 68;
+	public static final int WM_WINDOWPOSCHANGING = 70;
+	public static final int WM_WINDOWPOSCHANGED = 71;
+	public static final int WM_POWER = 72;
+	public static final int WM_COPYDATA = 74;
+	public static final int WM_CANCELJOURNAL = 75;
+	public static final int WM_NOTIFY = 78;
+	public static final int WM_INPUTLANGCHANGEREQUEST = 80;
+	public static final int WM_INPUTLANGCHANGE = 81;
+	public static final int WM_TCARD = 82;
+	public static final int WM_HELP = 83;
+	public static final int WM_USERCHANGED = 84;
+	public static final int WM_NOTIFYFORMAT = 85;
+	public static final int WM_CONTEXTMENU = 123;
+	public static final int WM_STYLECHANGING = 124;
+	public static final int WM_STYLECHANGED = 125;
+	public static final int WM_DISPLAYCHANGE = 126;
+	public static final int WM_GETICON = 127;
+	public static final int WM_SETICON = 128;
+	public static final int WM_NCCREATE = 129;
+	public static final int WM_NCDESTROY = 130;
+	public static final int WM_NCCALCSIZE = 131;
+	public static final int WM_NCHITTEST = 132;
+	public static final int WM_NCPAINT = 133;
+	public static final int WM_NCACTIVATE = 134;
+	public static final int WM_GETDLGCODE = 135;
+	public static final int WM_SYNCPAINT = 136;
+	public static final int WM_NCMOUSEMOVE = 160;
+	public static final int WM_NCLBUTTONDOWN = 161;
+	public static final int WM_NCLBUTTONUP = 162;
+	public static final int WM_NCLBUTTONDBLCLK = 163;
+	public static final int WM_NCRBUTTONDOWN = 164;
+	public static final int WM_NCRBUTTONUP = 165;
+	public static final int WM_NCRBUTTONDBLCLK = 166;
+	public static final int WM_NCMBUTTONDOWN = 167;
+	public static final int WM_NCMBUTTONUP = 168;
+	public static final int WM_NCMBUTTONDBLCLK = 169;
+	public static final int WM_NCXBUTTONDOWN = 171;
+	public static final int WM_NCXBUTTONUP = 172;
+	public static final int WM_NCXBUTTONDBLCLK = 173;
+	public static final int WM_INPUT = 255;
+	public static final int WM_KEYDOWN = 256;
+	public static final int WM_KEYFIRST = 256;
+	public static final int WM_KEYUP = 257;
+	public static final int WM_CHAR = 258;
+	public static final int WM_DEADCHAR = 259;
+	public static final int WM_SYSKEYDOWN = 260;
+	public static final int WM_SYSKEYUP = 261;
+	public static final int WM_SYSCHAR = 262;
+	public static final int WM_SYSDEADCHAR = 263;
+	public static final int WM_KEYLAST = 264;
+	public static final int WM_WNT_CONVERTREQUESTEX = 265;
+	public static final int WM_CONVERTREQUEST = 266;
+	public static final int WM_CONVERTRESULT = 267;
+	public static final int WM_INTERIM = 268;
+	public static final int WM_IME_STARTCOMPOSITION = 269;
+	public static final int WM_IME_ENDCOMPOSITION = 270;
+	public static final int WM_IME_COMPOSITION = 271;
+	public static final int WM_IME_KEYLAST = 271;
+	public static final int WM_INITDIALOG = 272;
+	public static final int WM_COMMAND = 273;
+	public static final int WM_SYSCOMMAND = 274;
+	public static final int WM_TIMER = 275;
+	public static final int WM_HSCROLL = 276;
+	public static final int WM_VSCROLL = 277;
+	public static final int WM_INITMENU = 278;
+	public static final int WM_INITMENUPOPUP = 279;
+	public static final int WM_MENUSELECT = 287;
+	public static final int WM_MENUCHAR = 288;
+	public static final int WM_ENTERIDLE = 289;
+	public static final int WM_MENURBUTTONUP = 290;
+	public static final int WM_MENUDRAG = 291;
+	public static final int WM_MENUGETOBJECT = 292;
+	public static final int WM_UNINTMENUPOPUP = 293;
+	public static final int WM_MENUCOMMAND = 294;
+	public static final int WM_CHANGEUISTATE = 295;
+	public static final int WM_UPDATEUISTATE = 296;
+	public static final int WM_QUERYUISTATE = 297;
+	public static final int WM_CTLCOLORMSGBOX = 306;
+	public static final int WM_CTLCOLOREDIT = 307;
+	public static final int WM_CTLCOLORLISTBOX = 308;
+	public static final int WM_CTLCOLORBTN = 309;
+	public static final int WM_CTLCLOLORDLG = 310;
+	public static final int WM_CTLCOLORSCROLLBAR = 311;
+	public static final int WM_CTLCOLORSTATIC = 312;
+	public static final int WM_MOUSEFIRST = 512;
+	public static final int WM_MOUSEMOVE = 512;
+	public static final int WM_LBUTTONDOWN = 513;
+	public static final int WM_LBUTTONUP = 514;
+	public static final int WM_LBUTTONDBLCLK = 515;
+	public static final int WM_RBUTTONDOWN = 516;
+	public static final int WM_RBUTTONUP = 517;
+	public static final int WM_RBUTTONDBLCLK = 518;
+	public static final int WM_MBUTTONDOWN = 519;
+	public static final int WM_MBUTTONUP = 520;
+	public static final int WM_MBUTTONDBLCLK = 521;
+	public static final int WM_MOUSELAST = 521;
+	public static final int WM_MOUSEWHEEL = 522;
+	public static final int WM_XBUTTONDOWN = 523;
+	public static final int WM_XBUTTONUP = 524;
+	public static final int WM_XBUTTONDBLCLK = 525;
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MessageHandler.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MessageHandler.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/MessageHandler.java	(revision 432)
@@ -0,0 +1,55 @@
+package de.ugoe.cs.quest.windows;
+
+/**
+ * <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>
+	 * Constructor. Protected to prohibit initialization of the base class
+	 * itself.
+	 * </p>
+	 */
+	protected MessageHandler() {
+	}
+
+	/**
+	 * <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() {
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/SequenceSplitter.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/SequenceSplitter.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/SequenceSplitter.java	(revision 432)
@@ -0,0 +1,287 @@
+package de.ugoe.cs.quest.windows;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.quest.data.Event;
+import de.ugoe.cs.quest.windows.data.WindowsEvent;
+import de.ugoe.cs.quest.windows.data.WindowsMessage;
+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<WindowsEvent> actionSequence;
+
+	/**
+	 * <p>
+	 * Type of the previous message.
+	 * </p>
+	 */
+	private int prevMsg = 0;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new SequenceSplitter.
+	 * </p>
+	 */
+	public SequenceSplitter() {
+		currentSequence = new LinkedList<WindowsMessage>();
+		openDowns = 0;
+		initMessages = true;
+		tokenGenerator = new EventGenerator();
+		actionSequence = new LinkedList<WindowsEvent>();
+		prevMsg = 0;
+	}
+
+	/**
+	 * <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) {
+				WindowsEvent currentAction = tokenGenerator
+						.generateEvent(currentSequence);
+				if (currentAction != null) {
+					actionSequence.add(currentAction);
+				}
+				if (isKeyMessage(msg.getType()) && openDowns > 0) {
+					Console.traceln("Key message found with open down mouse messages - will probabably result in a faulty sequence.");
+				}
+			} else {
+				initMessages = false;
+			}
+			currentSequence = new LinkedList<WindowsMessage>();
+		}
+		if (isUpMessage(msg.getType())) {
+			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 == MessageDefs.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<WindowsEvent> 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() {
+		WindowsEvent 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;
+		int msgType = msg.getType();
+		if (isKeyMessage(msgType)) {
+			isStart = true;
+		}
+		if (isDownMessage(msgType)) {
+			openDowns++;
+			if (openDowns == 1) {
+				isStart = true;
+			}
+		}
+		if (isDblclkMessage(msgType)) {
+			openDowns++;
+		}
+		return isStart;
+	}
+
+	/**
+	 * <p>
+	 * Checks if the type of a message is generated is a keyboard interaction.
+	 * </p>
+	 * 
+	 * @param msgType
+	 *            type of the message
+	 * @return true if it is a keyboard interaction; false otherwise
+	 */
+	private boolean isKeyMessage(int msgType) {
+		boolean isKeyMsg = false;
+		switch (msgType) {
+		case MessageDefs.WM_KEYDOWN:
+		case MessageDefs.WM_KEYUP:
+		case MessageDefs.WM_SYSKEYDOWN:
+		case MessageDefs.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
+	 */
+	private boolean isDownMessage(int msgType) {
+		boolean isDownMsg = false;
+		switch (msgType) {
+		case MessageDefs.WM_LBUTTONDOWN:
+		case MessageDefs.WM_RBUTTONDOWN:
+		case MessageDefs.WM_MBUTTONDOWN:
+		case MessageDefs.WM_XBUTTONDOWN:
+		case MessageDefs.WM_NCLBUTTONDOWN:
+		case MessageDefs.WM_NCRBUTTONDOWN:
+		case MessageDefs.WM_NCMBUTTONDOWN:
+		case MessageDefs.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
+	 */
+	private boolean isDblclkMessage(int msgType) {
+		boolean isDblclkMsg = false;
+		switch (msgType) {
+		case MessageDefs.WM_LBUTTONDBLCLK:
+		case MessageDefs.WM_RBUTTONDBLCLK:
+		case MessageDefs.WM_MBUTTONDBLCLK:
+		case MessageDefs.WM_XBUTTONDBLCLK:
+		case MessageDefs.WM_NCLBUTTONDBLCLK:
+		case MessageDefs.WM_NCRBUTTONDBLCLK:
+		case MessageDefs.WM_NCMBUTTONDBLCLK:
+		case MessageDefs.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
+	 */
+	private boolean isUpMessage(int msgType) {
+		boolean isUpMsg = false;
+		switch (msgType) {
+		case MessageDefs.WM_LBUTTONUP:
+		case MessageDefs.WM_RBUTTONUP:
+		case MessageDefs.WM_MBUTTONUP:
+		case MessageDefs.WM_XBUTTONUP:
+		case MessageDefs.WM_NCLBUTTONUP:
+		case MessageDefs.WM_NCRBUTTONUP:
+		case MessageDefs.WM_NCMBUTTONUP:
+		case MessageDefs.WM_NCXBUTTONUP:
+			isUpMsg = true;
+			break;
+		default:
+			break;
+		}
+		return isUpMsg;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDconvertDirToXml.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDconvertDirToXml.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDconvertDirToXml.java	(revision 432)
@@ -0,0 +1,59 @@
+package de.ugoe.cs.quest.windows.commands;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.windows.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 void help() {
+		Console.println("Usage: convertToXml <sourceFolder> <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 InvalidParameterException();
+		}
+		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/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDconvertToXml.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDconvertToXml.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDconvertToXml.java	(revision 432)
@@ -0,0 +1,59 @@
+package de.ugoe.cs.quest.windows.commands;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import de.ugoe.cs.quest.windows.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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+		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/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDparseXML.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDparseXML.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/commands/CMDparseXML.java	(revision 432)
@@ -0,0 +1,89 @@
+package de.ugoe.cs.quest.windows.commands;
+
+import java.security.InvalidParameterException;
+import java.util.Collection;
+import java.util.List;
+import java.util.SortedSet;
+
+import de.ugoe.cs.quest.CommandHelpers;
+import de.ugoe.cs.quest.data.GlobalDataContainer;
+import de.ugoe.cs.quest.windows.MFCLogParser;
+import de.ugoe.cs.quest.windows.data.MFCTargetComparator;
+import de.ugoe.cs.quest.windows.data.WindowTree;
+import de.ugoe.cs.quest.windows.data.WindowsEvent;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <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 void help() {
+		Console.println("Usage: 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 InvalidParameterException();
+		}
+
+		MFCLogParser parser = new MFCLogParser(countMessageOccurences);
+		parser.parseFile(filename);
+
+		Collection<List<WindowsEvent>> sequences = parser.getSequences();
+		
+		Console.traceln("Pre-computing event target equalities.");
+		// compare all Events to a dummy event to make sure they are known by
+		// the MFCTargetComparator
+		WindowsEvent dummyEvent = new WindowsEvent("dummy");
+		for (List<WindowsEvent> sequence : sequences) {
+			for (WindowsEvent event : sequence) {
+				event.equals(dummyEvent);
+			}
+		}
+		MFCTargetComparator.setMutable(false);
+		
+		SortedSet<String> targets = WindowTree.getInstance().getTargets();
+
+		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
+			CommandHelpers.dataOverwritten(sequencesName);
+		}
+		if (GlobalDataContainer.getInstance().addData(
+				sequencesName + "_targets", targets)) {
+			CommandHelpers.dataOverwritten(sequencesName + "_targets");
+		}
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/MFCTargetComparator.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/MFCTargetComparator.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/MFCTargetComparator.java	(revision 432)
@@ -0,0 +1,412 @@
+package de.ugoe.cs.quest.windows.data;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.commons.collections15.CollectionUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * <p>
+ * This class implements a comparator for target strings of MFC GUIs. It
+ * internally maintains a collection of all targets that have been compared to
+ * ensure the transitivity of the equals relation. This memory can always be
+ * deleted by calling {@link #reset()}.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class MFCTargetComparator {
+
+	/**
+	 * <p>
+	 * If mutable is true, new target strings can be added to the internal
+	 * memory. This leads to a very expensive {@link #compare(String, String)}
+	 * operation.
+	 * </p>
+	 * <p>
+	 * if mutable is set to false, currently possible equal targets are
+	 * pre-computed. This pre-computation is expensive and might take a while.
+	 * In turn, the {@link #compare(String, String)} operation becomes very
+	 * cheap.
+	 * </p>
+	 */
+	private static boolean mutable = true;
+
+	/**
+	 * <p>
+	 * Set of all currently known targets.
+	 * </p>
+	 */
+	private static Set<String> knownTargets = new LinkedHashSet<String>();
+
+	/**
+	 * <p>
+	 * Map that contains for all known target strings all equal target strings.
+	 * Pre-computed when {@link #mutable} is set to false.
+	 * </p>
+	 */
+	private static Map<String, Set<String>> equalTargets;
+
+	/**
+	 * <p>
+	 * Changes the mutability of the comparator. If the mutability is changed
+	 * from true to false, the map {@link #equalTargets} is computed.
+	 * </p>
+	 * 
+	 * @param mutable
+	 *            new mutability of the comparator
+	 */
+	public static void setMutable(boolean mutable) {
+		if (MFCTargetComparator.mutable == true && mutable == false) {
+			equalTargets = new HashMap<String, Set<String>>();
+			for (String target1 : knownTargets) {
+				Set<String> curEqualTargets = new HashSet<String>();
+				for (String target2 : knownTargets) {
+					if (compare(target1, target2)) {
+						curEqualTargets.add(target2);
+					}
+				}
+				equalTargets.put(target1, curEqualTargets);
+			}
+		}
+		MFCTargetComparator.mutable = mutable;
+	}
+
+	/**
+	 * <p>
+	 * Compares to target strings. The strings are equal, if TODO
+	 * <ul>
+	 * <li>the class, resourceId, and modality of all widgets are equal</li>
+	 * <li>either the name or the hashCode of all widgets are equal</li>
+	 * <li>either the name or the hashCode has been observed in one equal
+	 * instance of a widget, for all widgets.</li>
+	 * </ul>
+	 * </p>
+	 * <p>
+	 * All target strings are remembered internally, to be able to test for the
+	 * third property.
+	 * </p>
+	 * 
+	 * @param target1
+	 *            first target string
+	 * @param target2
+	 *            second target string
+	 * @return true, if both targets are equal; false otherwise
+	 */
+	public static boolean compare(String target1, String target2) {
+		boolean result = false;
+		if (mutable) {
+			try {
+				MFCWidget widget1 = null;
+				MFCWidget widget2 = null;
+				if (!"dummy".equals(target1)) {
+					instance.addTarget(target1);
+					knownTargets.add(target1);
+					widget1 = instance.find(target1);
+				}
+				if (!"dummy".equals(target2)) {
+					instance.addTarget(target2);
+					knownTargets.add(target2);
+					widget2 = instance.find(target2);
+				}
+				if (widget1 == null) {
+					return false;
+				}
+				result = (widget1 == widget2);
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+		if (!mutable) {
+			Set<String> curEquals = equalTargets.get(target1);
+			if (curEquals != null) {
+				result = curEquals.contains(target2);
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * <p>
+	 * Resets the internal memory of targets.
+	 * </p>
+	 */
+	public static void reset() {
+		instance = new MFCTargetComparator();
+	}
+
+	/**
+	 * <p>
+	 * Internal handle to the instance of this class (implemented as
+	 * Singleton!).
+	 * </p>
+	 */
+	private static MFCTargetComparator instance = new MFCTargetComparator();
+
+	/**
+	 * <p>
+	 * Private Constructor. Creates a new instance of the class and prevents
+	 * instantiation from outside of this class.
+	 * </p>
+	 */
+	private MFCTargetComparator() {
+	}
+
+	/**
+	 * <p>
+	 * List of the root widgets found in the target string.
+	 * </p>
+	 */
+	private List<MFCWidget> rootWidgets = new ArrayList<MFCTargetComparator.MFCWidget>();
+
+	/**
+	 * <p>
+	 * Adds a target to the memory.
+	 * </p>
+	 * 
+	 * @param target
+	 *            target to be added
+	 */
+	private void addTarget(String target) throws Exception {
+		if (target != null) {
+			DocumentBuilder documentBuilder = DocumentBuilderFactory
+					.newInstance().newDocumentBuilder();
+			Document doc = documentBuilder.parse(new ByteArrayInputStream(
+					("<dummy>" + target + "</dummy>").getBytes("UTF-8")));
+			doc.getDocumentElement().normalize();
+			NodeList widgets = doc.getElementsByTagName("window");
+
+			MFCWidget parent = null;
+			for (int i = 0; i < widgets.getLength(); i++) {
+				Element currentWidget = (Element) widgets.item(i);
+				parent = addWidget(currentWidget, parent);
+			}
+		}
+	}
+
+	/**
+	 * <p>
+	 * Adds a widget extracted from a target to the memory. The widget is placed
+	 * as a child/parent of other widget according to the GUI hierarchy of the
+	 * application.
+	 * </p>
+	 * <p>
+	 * In case the widget already exists, the existing widget is returned and
+	 * the known targets and hashCodes of the existing widget are updated.
+	 * </p>
+	 * 
+	 * @param widgetString
+	 *            string identifying the widget
+	 * @param parent
+	 *            parent widget; if null, it is a root widget and added to
+	 *            {@link #rootWidgets}
+	 * @return the created widget.
+	 */
+	private MFCWidget addWidget(Element widgetElement, MFCWidget parent) {
+		MFCWidget widget = generateWidget(widgetElement);
+
+		if (parent == null) {
+			int index = rootWidgets.indexOf(widget);
+			if (index >= 0) {
+				widget = rootWidgets.get(index);
+				widget.names.add(widgetElement.getAttribute("name"));
+				widget.hwnds.add(widgetElement.getAttribute("hwnd"));
+			} else {
+				rootWidgets.add(widget);
+			}
+		} else {
+			int index = parent.children.indexOf(widget);
+			if (index >= 0) {
+				widget = parent.children.get(index);
+				widget.names.add(widgetElement.getAttribute("name"));
+				widget.hwnds.add(widgetElement.getAttribute("hwnd"));
+			} else {
+				parent.children.add(widget);
+			}
+		}
+		return widget;
+	}
+
+	/**
+	 * <p>
+	 * Creates a new {@link MFCWidget} from a widget XML element.
+	 * </p>
+	 * 
+	 * @param widgetElement
+	 *            XML element containing information about the widget
+	 * @return created {@link MFCWidget}
+	 */
+	private MFCWidget generateWidget(Element widgetElement) {
+		MFCWidget widget = new MFCWidget();
+		widget.names.add(widgetElement.getAttribute("name"));
+		widget.hwnds.add(widgetElement.getAttribute("hwnd"));
+		widget.widgetClass = widgetElement.getAttribute("class");
+		widget.resourceId = widgetElement.getAttribute("resourceId");
+		widget.modality = widgetElement.getAttribute("isModal");
+		return widget;
+	}
+
+	/**
+	 * <p>
+	 * Tries to find the {@link MFCWidget} that the target string identifies in
+	 * the known GUI hierarchy, by traversing the known widgets starting with
+	 * the {@link #rootWidgets}.
+	 * 
+	 * @param target
+	 *            target string whose widget is searched for
+	 * @return respective {@link MFCWidget} instance if it is found; null
+	 *         otherwise
+	 */
+	private MFCWidget find(String target) throws Exception {
+		MFCWidget widget = null;
+		if (target != null) {
+			DocumentBuilder documentBuilder = DocumentBuilderFactory
+					.newInstance().newDocumentBuilder();
+			Document doc = documentBuilder.parse(new ByteArrayInputStream(
+					("<dummy>" + target + "</dummy>").getBytes("UTF-8")));
+			doc.getDocumentElement().normalize();
+			NodeList widgets = doc.getElementsByTagName("window");
+
+			MFCWidget parent = null;
+			for (int i = 0; i < widgets.getLength(); i++) {
+				Element currentWidget = (Element) widgets.item(i);
+				MFCWidget generatedWidget = generateWidget(currentWidget);
+				if (parent == null) {
+					int index = rootWidgets.indexOf(generatedWidget);
+					if (index >= 0) {
+						parent = rootWidgets.get(index);
+					} else {
+						return null;
+					}
+				} else {
+					int index = parent.children.indexOf(generatedWidget);
+					if (index >= 0) {
+						parent = parent.children.get(index);
+					} else {
+						return null;
+					}
+				}
+			}
+			widget = parent;
+		}
+		return widget;
+	}
+
+	/**
+	 * <p>
+	 * Internal class used to store MFCWidget. The implementation is more like a
+	 * C-style structure, than an actual class.
+	 * </p>
+	 * 
+	 * @author Steffen Herbold
+	 * @version 1.0
+	 */
+	private static class MFCWidget {
+
+		/**
+		 * <p>
+		 * Set of all known name strings of the widget.
+		 * </p>
+		 */
+		Set<String> names = new LinkedHashSet<String>();
+
+		/**
+		 * <p>
+		 * Set of all known hwnds of the widget.
+		 * </p>
+		 */
+		Set<String> hwnds = new LinkedHashSet<String>();
+
+		/**
+		 * <p>
+		 * Class of the widget.
+		 * </p>
+		 */
+		String widgetClass;
+
+		/**
+		 * <p>
+		 * Resource id of the widget.
+		 * </p>
+		 */
+		String resourceId;
+
+		/**
+		 * <p>
+		 * Modality of the widget.
+		 * </p>
+		 */
+		String modality;
+
+		/**
+		 * <p>
+		 * Pre-computed hash code of the widget.
+		 * </p>
+		 */
+		int hashCode = 0;
+
+		/**
+		 * <p>
+		 * List of children of the widget.
+		 * </p>
+		 */
+		List<MFCWidget> children = new ArrayList<MFCTargetComparator.MFCWidget>();
+
+		/**
+		 * <p>
+		 * Two widgets are equal, if {@link #widgetClass}, {@link #resourceId},
+		 * and {@link #modality} are equal and the intersection of either the
+		 * {@link #hwnds}, the {@link #names}, or both of them is not empty.
+		 * </p>
+		 * 
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		@Override
+		public boolean equals(Object obj) {
+			if (obj instanceof MFCWidget) {
+				MFCWidget other = (MFCWidget) obj;
+				boolean titleEqual = CollectionUtils.containsAny(names,
+						other.names);
+				boolean hashEqual = CollectionUtils.containsAny(hwnds,
+						other.hwnds);
+
+				return widgetClass.equals(other.widgetClass)
+						&& resourceId.equals(other.resourceId)
+						&& modality.equals(other.modality)
+						&& (titleEqual || hashEqual);
+			}
+			return false;
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.Object#hashCode()
+		 */
+		@Override
+		public int hashCode() {
+			if (hashCode == 0) {
+				int multiplier = 17;
+				hashCode = multiplier * hashCode + widgetClass.hashCode();
+				hashCode = multiplier * hashCode + resourceId.hashCode();
+				hashCode = multiplier * hashCode + modality.hashCode();
+			}
+			return hashCode;
+		}
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowTree.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowTree.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowTree.java	(revision 432)
@@ -0,0 +1,188 @@
+package de.ugoe.cs.quest.windows.data;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * <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>
+ * <p>
+ * The class is implemented as a singleton. The rational behind implementing
+ * this class as a singleton is to ease the access of all class that may request
+ * information about the windows during the parsing of a session. As the tree
+ * may change during the session, it does not make sense to preserve it after a
+ * session. Thus, it can just be deleted. Therefore, as long as only one session
+ * is parsed at a time, a single instance is sufficient.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WindowTree {
+
+	/**
+	 * <p>
+	 * Handle to the window instance.
+	 * </p>
+	 */
+	private static WindowTree theInstance = null;
+
+	/**
+	 * <p>
+	 * Maintains a set of all the target strings of all widgets that were at
+	 * some point part of the window tree.
+	 * </p>
+	 */
+	private SortedSet<String> targets;
+
+	/**
+	 * <p>
+	 * Obtain a handle to the window instance.
+	 * </p>
+	 * 
+	 * @return instance of the window tree
+	 */
+	public static WindowTree getInstance() {
+		if (theInstance == null) {
+			theInstance = new WindowTree();
+		}
+		return theInstance;
+	}
+
+	/**
+	 * <p>
+	 * Resets the tree. Should be used between sessions.
+	 * </p>
+	 */
+	public static void resetTree() {
+		theInstance = null;
+	}
+
+	/**
+	 * <p>
+	 * Map of all windows that are part of the tree for efficient searching. The
+	 * keys of the map are the hwnd's of the windows.
+	 * </p>
+	 */
+	private Map<Integer, WindowTreeNode> nodes;
+
+	/**
+	 * <p>
+	 * Creates a new WindowTree.
+	 * </p>
+	 * <p>
+	 * Private, as the class is a singleton.
+	 * </p>
+	 */
+	private WindowTree() {
+		nodes = new HashMap<Integer, WindowTreeNode>();
+		targets = new TreeSet<String>();
+	}
+
+	/**
+	 * <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(int parentHwnd, int childHwnd, String childWindowName,
+			int resourceId, String className, boolean isModal) {
+		WindowTreeNode parent = nodes.get(parentHwnd);
+		WindowTreeNode child = nodes.get(childHwnd);
+		if (child == null) {
+			if (parent != null) {
+				child = parent.addChild(childHwnd, childWindowName, resourceId,
+						className, isModal);
+			} else {
+				child = new WindowTreeNode(childHwnd, null, childWindowName,
+						resourceId, className, isModal);
+			}
+			nodes.put(childHwnd, child);
+			targets.add(child.xmlRepresentation());
+		}
+	}
+
+	/**
+	 * <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(int hwnd) {
+		int removedCounter = 0;
+		WindowTreeNode node = nodes.get(hwnd);
+		if (node != null) {
+			List<WindowTreeNode> nodesToBeRemoved = node.remove();
+			for (int i = 0; i < nodesToBeRemoved.size(); i++) {
+				WindowTreeNode nodeToBeRemoved = nodesToBeRemoved.get(i);
+				nodesToBeRemoved.addAll(nodeToBeRemoved.getChildren());
+				nodes.remove(nodeToBeRemoved.getHwnd());
+				removedCounter++;
+			}
+			nodes.remove(hwnd);
+			removedCounter++;
+		}
+		return removedCounter;
+	}
+
+	/**
+	 * <p>
+	 * Searches the tree for a window with the specified hwnd and returns its
+	 * {@link WindowTreeNode}.
+	 * </p>
+	 * 
+	 * @param hwnd
+	 *            hwnd that is looked for
+	 * @return {@link WindowTreeNode} of the window with the given hwnd if
+	 *         found, null otherwise
+	 */
+	public WindowTreeNode find(int hwnd) {
+		return nodes.get(hwnd);
+	}
+
+	/**
+	 * <p>
+	 * Returns the number of nodes contained in the WindowTree.
+	 * </p>
+	 * 
+	 * @return number of nodes
+	 */
+	public int size() {
+		return nodes.size();
+	}
+
+	/**
+	 * <p>
+	 * Returns a sorted set of all targets that existed any time in the window
+	 * tree.
+	 * </p>
+	 * 
+	 * @return set of targets
+	 */
+	public SortedSet<String> getTargets() {
+		return targets;
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowTreeNode.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowTreeNode.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowTreeNode.java	(revision 432)
@@ -0,0 +1,292 @@
+package de.ugoe.cs.quest.windows.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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 WindowTreeNode {
+
+	/**
+	 * <p>
+	 * Name of the window. May change over time.
+	 * </p>
+	 */
+	private String windowName;
+
+	/**
+	 * <p>
+	 * Handle of the window. Used as unique identifier during its existence.
+	 * </p>
+	 */
+	private final int hwnd;
+
+	/**
+	 * <p>
+	 * Resource id of the window.
+	 * </p>
+	 */
+	private final int resourceId;
+
+	/**
+	 * <p>
+	 * Class name of the window.
+	 * </p>
+	 */
+	private final String className;
+
+	/**
+	 * <p>
+	 * True, if the window is modal.
+	 * </p>
+	 */
+	private final boolean isModal;
+
+	/**
+	 * <p>
+	 * Parent of the window. <code>null</code> if the window has no parent.
+	 * </p>
+	 */
+	private WindowTreeNode parent;
+
+	/**
+	 * <p>
+	 * List of the windows children. May be empty.
+	 * </p>
+	 */
+	private List<WindowTreeNode> children;
+
+	/**
+	 * <p>
+	 * Creates a new WindowTreeNode.
+	 * </p>
+	 * <p>
+	 * The constructor is protected WindowTreeNode may only be created from the
+	 * WindowTree.
+	 * </p>
+	 * 
+	 * @param hwnd
+	 *            hwnd of the window
+	 * @param parent
+	 *            reference to the parent's WindowTreeNode
+	 * @param windowName
+	 *            name of the window
+	 * @param resourceId
+	 *            resource id of the window
+	 * @param className
+	 *            class name of the window
+	 * @param isModal
+	 *            modality of the window
+	 */
+	protected WindowTreeNode(int hwnd, WindowTreeNode parent,
+			String windowName, int resourceId, String className, boolean isModal) {
+		this.hwnd = hwnd;
+		this.parent = parent;
+		this.windowName = windowName;
+		this.resourceId = resourceId;
+		this.className = className;
+		this.isModal = isModal;
+		children = new ArrayList<WindowTreeNode>();
+	}
+
+	/**
+	 * <p>
+	 * Returns a reference to the WindowTreeNode of the parent.
+	 * </p>
+	 * 
+	 * @return WindowTreeNode of the parent
+	 */
+	public WindowTreeNode getParent() {
+		return parent;
+	}
+
+	/**
+	 * <p>
+	 * Returns the list of the windows children.
+	 * </p>
+	 * 
+	 * @return list of the windows children
+	 */
+	public List<WindowTreeNode> getChildren() {
+		return children;
+	}
+
+	/**
+	 * <p>
+	 * Returns the name of the window.
+	 * </p>
+	 * 
+	 * @return name of the window
+	 */
+	public String getName() {
+		return windowName;
+	}
+
+	/**
+	 * <p>
+	 * Returns the hwnd of the window.
+	 * </p>
+	 * 
+	 * @return hwnd of the window
+	 */
+	public int getHwnd() {
+		return hwnd;
+	}
+
+	/**
+	 * <p>
+	 * Returns the resource id of the window.
+	 * </p>
+	 * 
+	 * @return resource id of the window
+	 */
+	public int getResourceId() {
+		return resourceId;
+	}
+
+	/**
+	 * <p>
+	 * Returns the class name of the window.
+	 * </p>
+	 * 
+	 * @return class name of the window
+	 */
+	public String getClassName() {
+		return className;
+	}
+
+	/**
+	 * <p>
+	 * Sets the name of the window.
+	 * </p>
+	 * 
+	 * @param text
+	 *            new name of the window
+	 */
+	public void setName(String text) {
+		windowName = text;
+	}
+
+	/**
+	 * <p>
+	 * Removes a the window and all its children from the {@link WindowTree}.
+	 * </p>
+	 * 
+	 * @return list of the children of the window for further clean up.
+	 */
+	public List<WindowTreeNode> remove() {
+		if (parent != null) {
+			parent.removeChild(this);
+		}
+		return children;
+	}
+
+	/**
+	 * <p>
+	 * Removes a child window.
+	 * </p>
+	 * 
+	 * @param child
+	 *            reference to the child window to be removed
+	 */
+	public void removeChild(WindowTreeNode child) {
+		children.remove(child);
+	}
+
+	/**
+	 * <p>
+	 * Adds a new child window and creates WindowTreeNode for it.
+	 * </p>
+	 * 
+	 * @param childHwnd
+	 *            hwnd of the child window
+	 * @param childWindowName
+	 *            name of the child window
+	 * @param resourceId
+	 *            resource id of the child window
+	 * @param className
+	 *            class name of the child window
+	 * @param isModal
+	 *            modality of the child window
+	 * @return reference to the WindowTreeNode created for the child window
+	 */
+	public WindowTreeNode addChild(int childHwnd, String childWindowName,
+			int resourceId, String className, boolean isModal) {
+		WindowTreeNode child = new WindowTreeNode(childHwnd, this,
+				childWindowName, resourceId, className, isModal);
+		children.add(child);
+		return child;
+	}
+
+	/**
+	 * <p>
+	 * Returns a string identfier of the window:<br>
+	 * {@code [resourceId;"windowName";"className";modality]}
+	 * </p>
+	 * 
+	 * @return identifier string of the window
+	 */
+	@Override
+	public String toString() {
+		return "[" + resourceId + ";\"" + windowName + "\";\"" + className
+				+ "\";" + isModal + "]";
+	}
+
+	/**
+	 * <p>
+	 * Returns an XML representation of the window, including its parents. It is
+	 * defined as follows:<br>
+	 * <code>
+	 * parent#xmlRepresentation()<br>
+	 * &lt;window name="this.windowname" class="this.className" resourceId="this.resourceId" isModal="this.isModel"/&gt;
+	 * </code>
+	 * </p>
+	 * 
+	 * @return xml representation of the window
+	 */
+	public String xmlRepresentation() {
+		String xmlString = "";
+		if (parent != null) {
+			xmlString = parent.xmlRepresentation();
+		}
+		xmlString += "<window name=\""
+				+ StringTools.xmlEntityReplacement(windowName) + "\" class=\""
+				+ StringTools.xmlEntityReplacement(className)
+				+ "\" resourceId=\"" + resourceId + "\" isModal=\"" + isModal
+				+ "\" hwnd=\"" + hwnd + "\""
+				+ "/>";
+		return xmlString;
+	}
+
+	/**
+	 * <p>
+	 * Returns the names of the parents and itself separated by dots, e.g.,
+	 * "GrandParent.Parent.windowName"
+	 * </p>
+	 * 
+	 * @return names of the parents separated by dots
+	 */
+	public String getParentNames() {
+		String parentNames = "";
+		if (parent != null) {
+			parentNames = parent.getParentNames() + ".";
+		}
+		parentNames += windowName;
+		return parentNames;
+	}
+
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowsEvent.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowsEvent.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowsEvent.java	(revision 432)
@@ -0,0 +1,86 @@
+package de.ugoe.cs.quest.windows.data;
+
+import java.io.ByteArrayInputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import de.ugoe.cs.quest.data.ReplayableEvent;
+
+/**
+ * <p>
+ * Convenience class for working with Windows MFC events.
+ * </p>
+ * 
+ * @author Steffen Herbold
+ * @version 1.0
+ */
+public class WindowsEvent extends ReplayableEvent<WindowsMessage> {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new WindowEvent.
+	 * </p>
+	 * 
+	 * @see de.ugoe.cs.quest.data.Event#Event(String)
+	 * @param type
+	 *            type of the event.
+	 */
+	public WindowsEvent(String type) {
+		super(type);
+	}
+
+	@Override
+	protected boolean targetEquals(String otherTarget) {
+		return MFCTargetComparator.compare(target, otherTarget);
+	}
+	
+	int targetHash = 0;
+	
+	@Override
+	protected int targetHashCode() {
+		if( targetHash==0 ) {
+			int multiplier = 17;
+			if (target != null) {
+				Document doc;
+				try {
+					DocumentBuilder documentBuilder = DocumentBuilderFactory
+							.newInstance().newDocumentBuilder();
+					doc = documentBuilder.parse(new ByteArrayInputStream(
+							("<dummy>" + target + "</dummy>").getBytes("UTF-8")));
+				} catch (Exception e) {
+					e.printStackTrace();
+					return 0;
+				}
+				doc.getDocumentElement().normalize();
+				NodeList widgets = doc.getElementsByTagName("window");
+
+				for (int i = 0; i < widgets.getLength(); i++) {
+					Element currentWidget = (Element) widgets.item(i);
+					targetHash = targetHash* multiplier + widgetHashCode(currentWidget);
+				}
+			}
+		}
+		return targetHash;
+	}
+	
+	private int widgetHashCode(Element currentWidget) {
+		int hashCode = 0;
+		int multiplier = 41;
+		hashCode = hashCode * multiplier + currentWidget.getAttribute("class").hashCode();
+		hashCode = hashCode * multiplier + currentWidget.getAttribute("resourceId").hashCode();
+		hashCode = hashCode * multiplier + currentWidget.getAttribute("isModal").hashCode();
+		return hashCode;
+	}
+}
Index: /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowsMessage.java
===================================================================
--- /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowsMessage.java	(revision 432)
+++ /trunk/quest-ui-core/src/de/ugoe/cs/quest/windows/data/WindowsMessage.java	(revision 432)
@@ -0,0 +1,520 @@
+package de.ugoe.cs.quest.windows.data;
+
+import java.security.InvalidParameterException;
+import java.util.HashMap;
+import java.util.Map;
+
+import de.ugoe.cs.quest.data.IReplayable;
+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 WindowsMessage implements IReplayable {
+
+	/**
+	 * <p>
+	 * Id for object serialization.
+	 * </p>
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <p>
+	 * Type of the message.
+	 * </p>
+	 */
+	final int type;
+
+	/**
+	 * <p>
+	 * Window class of the message target. Default: ""
+	 * </p>
+	 */
+	private String windowClass = "";
+
+	/**
+	 * <p>
+	 * Resource Id of the message target. Default: 0
+	 * </p>
+	 */
+	private int resourceId = 0;
+
+	/**
+	 * <p>
+	 * XML representation of the message target.
+	 * </p>
+	 */
+	private String xmlWindowDescription = "";
+
+	/**
+	 * <p>
+	 * String that contains the names of all parent widgets and itself, separated by dots,
+	 * e.g., "GrandParent.Parent.self".
+	 * </p>
+	 */
+	private String parentNames = null;
+
+	/**
+	 * <p>
+	 * String that contains the window class of the parent widget.
+	 * </p>
+	 */
+	private String parentClass = null;
+
+	/**
+	 * <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>
+	 * 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>
+	 * 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, String> params = new HashMap<String, String>();
+
+	/**
+	 * <p>
+	 * Constructor. Creates a new message with a given message type.
+	 * </p>
+	 * 
+	 * @param type
+	 *            type of the message
+	 */
+	public WindowsMessage(int 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, String value) {
+		params.put(type, value);
+		if (type.equals("LPARAM")) {
+			LPARAM = Long.parseLong(value);
+		} else if (type.equals("WPARAM")) {
+			WPARAM = Long.parseLong(value);
+		}
+	}
+
+	/**
+	 * <p>
+	 * Returns the type of the message.
+	 * </p>
+	 * 
+	 * @return type of the message
+	 */
+	public int getType() {
+		return type;
+	}
+
+	/**
+	 * <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 String getParameter(String type) {
+		return params.get(type);
+	}
+
+	/**
+	 * <p>
+	 * Returns the window class of the message target.
+	 * </p>
+	 * 
+	 * @return window class of the message target
+	 */
+	public String getWindowClass() {
+		return windowClass;
+	}
+
+	/**
+	 * <p>
+	 * Returns the HWND the message is addressed to.
+	 * </p>
+	 * 
+	 * @return HWND the message is addressed to
+	 */
+	public int getHwnd() {
+		int hwnd = -1;
+		String hwndString = getParameter("window.hwnd"); // possible, as
+															// "window.hwnd" is
+															// mandatory
+		if (hwndString != null) {
+			hwnd = Integer.parseInt(hwndString);
+		}
+		return hwnd;
+	}
+
+	/**
+	 * <p>
+	 * Returns the resource Id of the message target.
+	 * </p>
+	 * 
+	 * @return resource Id of the message target
+	 */
+	public int getWindowResourceId() {
+		return resourceId;
+	}
+
+	/**
+	 * <p>
+	 * Two {@link WindowsMessage} are equal, if their {@link #type},
+	 * {@link #xmlWindowDescription}, 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).xmlWindowDescription
+							.equals(this.xmlWindowDescription)
+					&& ((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;
+		hash = multiplier * hash + xmlWindowDescription.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>
+	 * Retrieves the target string of a message from a given {@link WindowTree}
+	 * through looking up the HWND the message is addressed to in the window
+	 * tree.
+	 * </p>
+	 * 
+	 * @param windowTree
+	 *            {@link WindowTree} from which the target is extracted
+	 * @throws InvalidParameterException
+	 *             thrown if HWND is not contained in windowTree
+	 */
+	public void setTarget(WindowTree windowTree)
+			throws InvalidParameterException {
+		int hwnd = Integer.parseInt(getParameter("window.hwnd"));
+		WindowTreeNode node = windowTree.find(hwnd);
+		if (node == null) {
+			throw new InvalidParameterException("No window with HWND " + hwnd
+					+ " found in window tree!");
+		} else {
+			windowClass = node.getClassName();
+			resourceId = node.getResourceId();
+			xmlWindowDescription = node.xmlRepresentation();
+			parentNames = node.getParentNames();
+			WindowTreeNode parent = node.getParent();
+			if (parent == null) {
+				parentClass = "";
+			} else {
+				parentClass = parent.getClassName();
+			}
+		}
+	}
+
+	/**
+	 * <p>
+	 * Sets the LPARAM of a message.
+	 * </p>
+	 * 
+	 * @param paramValue
+	 *            value of the LPARAM
+	 */
+	public void setLPARAM(long paramValue) {
+		LPARAM = paramValue;
+	}
+
+	/**
+	 * <p>
+	 * Sets the WPARAM of a message.
+	 * </p>
+	 * 
+	 * @param paramValue
+	 *            value of the WPARAM
+	 */
+	public void setWPARAM(long paramValue) {
+		WPARAM = paramValue;
+	}
+
+	/**
+	 * <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>
+	 * 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>
+	 * Returns the target string of the message.
+	 * </p>
+	 * 
+	 * @return target string of the message
+	 */
+	public String getXmlWindowDescription() {
+		return xmlWindowDescription;
+	}
+
+	/**
+	 * <p>
+	 * Sets the target string manually.
+	 * </p>
+	 * 
+	 * @param xmlWindowDescription
+	 *            target string
+	 */
+	public void setXmlWindowDescription(String xmlWindowDescription) {
+		this.xmlWindowDescription = xmlWindowDescription;
+	}
+
+	/**
+	 * <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;
+	}
+
+	/**
+	 * <p>
+	 * Returns the parent names separated by dots, e.g., "GrandParent.Parent".
+	 * </p>
+	 * 
+	 * @return names of the parents
+	 */
+	public String getParentNames() {
+		return parentNames;
+	}
+
+	/**
+	 * <p>
+	 * Returns the window class of the parent.
+	 * </p>
+	 * 
+	 * @return window classes of the parents
+	 */
+	public String getParentClass() {
+		return parentClass;
+	}
+
+	/**
+	 * <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();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.IReplayable#getReplay()
+	 */
+	@Override
+	public String getReplay() {
+		StringBuilder currentMsgStr = new StringBuilder(400);
+		currentMsgStr.append("  <msg type=\"" + type + "\" ");
+		currentMsgStr.append("LPARAM=\"" + LPARAM + "\" ");
+		currentMsgStr.append("WPARAM=\"" + WPARAM + "\" ");
+		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(xmlWindowDescription);
+		currentMsgStr.append(StringTools.ENDLINE);
+		currentMsgStr.append("  </msg>");
+		currentMsgStr.append(StringTools.ENDLINE);
+		return currentMsgStr.toString();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see de.ugoe.cs.quest.data.IReplayable#getTarget()
+	 */
+	@Override
+	public String getTarget() {
+		return xmlWindowDescription;
+	}
+}
