Index: trunk/quest-ui-core/src/main/java/de/ugoe/cs/quest/plugin/jfc/JFCReplayIDCalculator.java
===================================================================
--- trunk/quest-ui-core/src/main/java/de/ugoe/cs/quest/plugin/jfc/JFCReplayIDCalculator.java	(revision 538)
+++ trunk/quest-ui-core/src/main/java/de/ugoe/cs/quest/plugin/jfc/JFCReplayIDCalculator.java	(revision 538)
@@ -0,0 +1,156 @@
+package de.ugoe.cs.quest.plugin.jfc;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.quest.plugin.jfc.eventcore.JFCEvent;
+
+/**
+ * <p>
+ * This class provides the functionality to calculate the unique GUITAR replayIDs
+ * for {@link JFCEvent}s. This code is mainly based on guitar source code: edu.umd.cs.guitar.
+ * model.JFCDefaultIDGeneratorSimple
+ *    
+ * </p>
+ * @author fabian.glaser
+ *
+ */
+
+public class JFCReplayIDCalculator {
+	
+	final int prime = 31;
+	
+	/**
+	 * Properties that are used to identify widgets
+	 */
+	private static List<String> ID_PROPERTIES = Arrays.asList(
+			"Class","Title","Icon");
+	
+	private static Map<String, Integer> ID_PROPERTIES_MAP;
+	static{
+		Map<String, Integer> idmap = new HashMap<String, Integer>(4);
+		idmap.put("Class", 1);
+		idmap.put("Title", 0);
+		idmap.put("Icon", 2);
+		idmap.put("Index", 3);
+		ID_PROPERTIES_MAP = Collections.unmodifiableMap(idmap);
+	}
+		
+	
+   /**
+    * Those classes are invisible widgets but cause false-positive when
+    * calculating ID (compare guitar source code: edu.umd.cs.guitar.
+    * model.JFCDefaultIDGeneratorSimple)
+    */
+   private static List<String> IGNORED_CLASSES = Arrays.asList("javax.swing.JPanel",
+         "javax.swing.JTabbedPane", "javax.swing.JScrollPane",
+         "javax.swing.JSplitPane", "javax.swing.Box",
+         "javax.swing.JViewport", "javax.swing.JScrollBar",
+         "javax.swing.JLayeredPane",
+         "javax.swing.JList$AccessibleJList$AccessibleJListChild",
+         "javax.swing.JList$AccessibleJList", "javax.swing.JList",
+         "javax.swing.JScrollPane$ScrollBar",
+         "javax.swing.plaf.metal.MetalScrollButton");
+	
+	
+	/**
+	 * Calculates the replayID needed for compatibility with Guitar suite of a JFCEvent
+	 * @param event for which the ID should be calculated
+	 * @return replayID
+	 */
+	public String calculateReplayID(JFCEvent event){
+		String replayID = "";
+		long hashCode = 1;
+		
+		// extract target string information
+		String target = event.getTarget();
+		String[] targetParts = target.split("\\]\\.\\[");
+		
+		// we assume that first component corresponds to the notion of a 
+		// window in GUITAR suite
+		
+		// calculate window hashcode
+		String[] windowInfo = targetParts[0].split("','");
+		String title = windowInfo[0];
+		String fuzzyTitle = getFuzzyTitle(title);
+		long windowHashCode = fuzzyTitle.hashCode();
+		windowHashCode = (windowHashCode * 2) & 0xffffffffL;
+		
+		long propagatedHashCode = windowHashCode;
+		
+		for (int i = 1; i < targetParts.length; i++){
+			long localHashCode = getLocalHashCode(targetParts[i]);
+			hashCode = propagatedHashCode * prime + localHashCode;
+	        hashCode = (hashCode * 2) & 0xffffffffL;
+			
+	        if (i < targetParts.length - 1){
+	        	Integer index = getIndex(targetParts[i+1]);
+				propagatedHashCode = prime * propagatedHashCode
+				+ index.hashCode();
+	        }
+		}
+		
+		replayID = "e" + hashCode;
+		
+		return replayID;
+	}
+	
+	/**
+	 * Calculates the hashcode part of one component.
+	 * @param targetPart A string that represents a compontent
+	 * @return the local hashcode
+	 */
+	
+	private long getLocalHashCode(String targetPart){
+		long hashcode = 1;
+		String[] widgetInfo = targetPart.split("','");
+		widgetInfo[0] = widgetInfo[0].substring(1);
+		int length = widgetInfo.length - 1;
+		widgetInfo[length] = widgetInfo[length].substring(0,widgetInfo[length].length()-1);
+		String wClass = widgetInfo[ID_PROPERTIES_MAP.get("Class")];
+		if (IGNORED_CLASSES.contains(wClass)) {
+		    hashcode = (prime * hashcode + (wClass.equals("null") ? 0 : (wClass
+		               .hashCode())));
+		    return hashcode;
+		}
+		else{
+			for (String property: ID_PROPERTIES){
+				String value = widgetInfo[ID_PROPERTIES_MAP.get(property)];
+				if (!value.equals("null")){
+					hashcode = prime * hashcode + property.hashCode();
+					hashcode = prime * hashcode + value.hashCode();
+				}
+			}
+		}
+		
+		hashcode = (hashcode * 2) & 0xffffffffL;
+		
+		return hashcode;
+	}
+	
+	/**
+	 * Guitar has a special way to deal with window titles when
+	 * calculating unique widget IDs. This method mimics Guitar's
+	 * behavior (compare compare guitar source code: edu.umd.cs.guitar.
+	 * model.JFCDefaultIDGeneratorSimple).
+	 * @param title
+	 * @return
+	 */
+	
+	private String getFuzzyTitle(String title){
+		return ".*FreeMind.*";
+	}
+	
+	private Integer getIndex(String targetPart){
+		String[] widgetInfo = targetPart.split("','");
+		String index = widgetInfo[ID_PROPERTIES_MAP.get("Index")];
+		if (index.equals("-1"))
+				throw new AssertionError("Index should only be -1 for components" +
+						"that have no parents.");
+		
+		return Integer.parseInt(index);
+	}
+};
