Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java	(revision 1301)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java	(revision 1335)
@@ -19,6 +19,9 @@
 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.autoquest.eventcore.IEventTarget;
@@ -27,11 +30,12 @@
 import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
 import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
-import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
-import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
-import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
-import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
 
 /**
@@ -41,5 +45,5 @@
  * @author 2012, last modified by $Author: pharms$
  */
-public class TextInputStatisticsRule implements de.ugoe.cs.autoquest.usability.UsabilityEvaluationRule {
+public class TextInputStatisticsRule implements UsabilityEvaluationRule {
 
     /*
@@ -51,5 +55,5 @@
     public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
         TextInputStatistics statistics = new TextInputStatistics();
-        calculateStatistics(taskModel.getTasks(), statistics);
+        calculateStatistics(taskModel.getUserSessions(), statistics);
 
         UsabilityEvaluationResult results = new UsabilityEvaluationResult();
@@ -97,9 +101,8 @@
         if (severity != null) {
             Map<String, String> parameters = new HashMap<String, String>();
-            parameters.put("textInputRatio", DecimalFormat.getInstance().format(ratio * 100) + "%");
+            parameters.put("textInputRatio", DecimalFormat.getInstance().format(ratio * 100));
 
             results.addDefect
-                (new UsabilityDefect(severity, UsabilityDefectDescription.TEXT_FIELD_INPUT_RATIO,
-                                     parameters));
+                (severity, UsabilityDefectDescription.TEXT_FIELD_INPUT_RATIO, parameters);
         }
     }
@@ -111,53 +114,58 @@
                                                 UsabilityEvaluationResult results)
     {
-        Map<String, Integer> words = new HashMap<String, Integer>();
-        int numberOfRepeatedWords = 0;
-        int maxRepetitions = 0;
-
-        for (int i = 0; i < statistics.getNoOfTextFieldInputs(); i++) {
-            String[] fragments = statistics.getTextFieldInputFragments(i);
-            for (String fragment : fragments) {
-                if (!"".equals(fragment.trim())) {
-                    Integer count = words.get(fragment);
-                    if (count == null) {
-                        words.put(fragment, 1);
-                    }
-                    else {
-                        count++;
-                        words.put(fragment, count);
-                        maxRepetitions = Math.max(count, maxRepetitions);
-
-                        if (count == 2) {
-                            // do not calculate repeated words several times
-                            numberOfRepeatedWords++;
-                        }
-                    }
-                }
-            }
-        }
-
+        List<TextFieldCorrelation> textFieldCorrelations =
+            statistics.determineTextFieldCorrelations();
+        
+        for (TextFieldCorrelation entry : textFieldCorrelations) {
+            int noOfUsagesOfTextField1 = statistics.getUsageCount(entry.textField1);
+            int noOfUsagesOfTextField2 = statistics.getUsageCount(entry.textField2);
+            int noOfUsagesOfTextField1WithSameTextInTextField2 = entry.enteredTexts.size();
+            
+            float ratioTextField1 =
+                noOfUsagesOfTextField1WithSameTextInTextField2 / (float) noOfUsagesOfTextField1;
+            
+            float ratioTextField2 =
+                noOfUsagesOfTextField1WithSameTextInTextField2 / (float) noOfUsagesOfTextField2;
+            
+            createTextFieldEntryRepetitionDefect
+                (ratioTextField1, entry.textField1, entry.textField2, results);
+            
+            createTextFieldEntryRepetitionDefect
+                (ratioTextField2, entry.textField2, entry.textField1, results);
+            
+        }
+    }
+
+    /**
+     *
+     */
+    private void createTextFieldEntryRepetitionDefect(float                     ratioOfEqualEntries,
+                                                      ITextField                textField1,
+                                                      ITextField                textField2,
+                                                      UsabilityEvaluationResult results)
+    {
         UsabilityDefectSeverity severity = null;
-        if ((numberOfRepeatedWords > 10) || (maxRepetitions > 10)) {
+        if (ratioOfEqualEntries > 0.9) {
             severity = UsabilityDefectSeverity.HIGH;
         }
-        else if ((numberOfRepeatedWords > 4) || (maxRepetitions > 4)) {
+        else if (ratioOfEqualEntries > 0.5) {
             severity = UsabilityDefectSeverity.MEDIUM;
         }
-        else if ((numberOfRepeatedWords > 2) || (maxRepetitions > 2)) {
+        else if (ratioOfEqualEntries > 0.2) {
             severity = UsabilityDefectSeverity.LOW;
         }
-        else if ((numberOfRepeatedWords > 1) || (maxRepetitions > 1)) {
+        else if (ratioOfEqualEntries > 0.1) {
             severity = UsabilityDefectSeverity.INFO;
         }
-
+        
         if (severity != null) {
             Map<String, String> parameters = new HashMap<String, String>();
-            parameters.put("textRepetitionRatio", numberOfRepeatedWords +
-                           " repeated tokens, up to " + maxRepetitions + " repetitions per token");
+            parameters.put("textRepetitionRatio",
+                           DecimalFormat.getInstance().format(ratioOfEqualEntries * 100));
+            parameters.put("textField1", textField1.toString());
+            parameters.put("textField2", textField2.toString());
 
             results.addDefect
-                (new UsabilityDefect(severity,
-                                     UsabilityDefectDescription.TEXT_FIELD_INPUT_REPETITIONS,
-                                     parameters));
+                (severity, UsabilityDefectDescription.TEXT_FIELD_INPUT_REPETITIONS, parameters);
         }
     }
@@ -169,14 +177,12 @@
                                                      UsabilityEvaluationResult results)
     {
-        int allCharactersCount = 0;
-        int noLetterOrDigitCount = 0;
-
-        for (int i = 0; i < statistics.getNoOfTextFieldInputs(); i++) {
-            String[] fragments = statistics.getTextFieldInputFragments(i);
-            for (String fragment : fragments) {
-                String effectiveFragment = fragment.trim();
-                for (int j = 0; j < effectiveFragment.length(); j++) {
-                    if (!Character.isWhitespace(effectiveFragment.charAt(j))) {
-                        if (!Character.isLetterOrDigit(effectiveFragment.charAt(j))) {
+        for (ITextField textField : statistics.getAllTextFields()) {
+            int allCharactersCount = 0;
+            int noLetterOrDigitCount = 0;
+
+            for (String textInput : statistics.getAllInputsInto(textField)) {
+                for (int j = 0; j < textInput.length(); j++) {
+                    if (!Character.isWhitespace(textInput.charAt(j))) {
+                        if (!Character.isLetterOrDigit(textInput.charAt(j))) {
                             noLetterOrDigitCount++;
                         }
@@ -185,35 +191,30 @@
                 }
             }
-        }
-
-        float ratio = (float) noLetterOrDigitCount / (float) allCharactersCount;
-
-        UsabilityDefectSeverity severity = null;
-        if (ratio > 0.1) // every 10th sign
-        {
-            severity = UsabilityDefectSeverity.HIGH;
-        }
-        else if (ratio > 0.05) // every 20th sign
-        {
-            severity = UsabilityDefectSeverity.MEDIUM;
-        }
-        else if (ratio > 0.02) // every 50th sign
-        {
-            severity = UsabilityDefectSeverity.LOW;
-        }
-        else if (ratio > 0.01) // every 100th sign
-        {
-            severity = UsabilityDefectSeverity.INFO;
-        }
-
-        if (severity != null) {
-            Map<String, String> parameters = new HashMap<String, String>();
-            parameters.put("noLetterOrDigitRatio", allCharactersCount + " entered characters of " +
-                           "which " + noLetterOrDigitCount + " were no letter or digit");
-
-            results.addDefect
-                (new UsabilityDefect(severity,
-                                     UsabilityDefectDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
-                                     parameters));
+
+            float ratio = (float) noLetterOrDigitCount / (float) allCharactersCount;
+
+            UsabilityDefectSeverity severity = null;
+            if (ratio > 0.1) { // every 10th sign
+                severity = UsabilityDefectSeverity.HIGH;
+            }
+            else if (ratio > 0.05) { // every 20th sign
+                severity = UsabilityDefectSeverity.MEDIUM;
+            }
+            else if (ratio > 0.02) { // every 50th sign
+                severity = UsabilityDefectSeverity.LOW;
+            }
+            else if (ratio > 0.01) { // every 100th sign
+                severity = UsabilityDefectSeverity.INFO;
+            }
+
+            if (severity != null) {
+                Map<String, String> parameters = new HashMap<String, String>();
+                parameters.put("textField", textField.toString());
+                parameters.put("noLetterOrDigitRatio",
+                               DecimalFormat.getInstance().format(ratio * 100));
+
+                results.addDefect
+                    (severity, UsabilityDefectDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO, parameters);
+            }
         }
     }
@@ -222,29 +223,46 @@
      * 
      */
-    private void calculateStatistics(Collection<ITask> tasks, TextInputStatistics statistics) {
-        for (ITask task : tasks) {
-            calculateStatistics(task, statistics);
-        }
-    }
-
-    /**
-     *
-     */
-    private void calculateStatistics(ITask task, TextInputStatistics statistics) {
-        
-        if (isTextInput(task)) {
-            calculateStatistics((IEventTask) task, statistics);
+    private void calculateStatistics(Collection<IUserSession> sessions,
+                                     TextInputStatistics      statistics)
+    {
+        System.out.print("calculating statistics ... ");
+        for (IUserSession session : sessions) {
+            for (ITaskInstance taskInstance : session) {
+                calculateStatistics(taskInstance, session, statistics);
+            }
+        }
+        System.out.println("done");
+    }
+
+    /**
+     *
+     */
+    private void calculateStatistics(ITaskInstance       taskInstance,
+                                     IUserSession        session,
+                                     TextInputStatistics statistics)
+    {
+        if (isTextInput(taskInstance)) {
+            calculateStatistics((IEventTaskInstance) taskInstance, session, statistics);
         }
         else {
-            if (task instanceof IStructuringTemporalRelationship) {
-                for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
-                    calculateStatistics(child, statistics);
-                }
-            }
-            else if (task instanceof IMarkingTemporalRelationship) {
+            if (taskInstance instanceof ISequenceInstance) {
+                for (ITaskInstance child : (ISequenceInstance) taskInstance) {
+                    calculateStatistics(child, session, statistics);
+                }
+            }
+            else if (taskInstance instanceof IIterationInstance) {
+                for (ITaskInstance child : (IIterationInstance) taskInstance) {
+                    calculateStatistics(child, session, statistics);
+                }
+            }
+            else if (taskInstance instanceof ISelectionInstance) {
                 calculateStatistics
-                    (((IMarkingTemporalRelationship) task).getMarkedTask(), statistics);
-            }
-            else {
+                    (((ISelectionInstance) taskInstance).getChild(), session, statistics);
+            }
+            else if (taskInstance instanceof IOptionalInstance) {
+                calculateStatistics
+                    (((IOptionalInstance) taskInstance).getChild(), session, statistics);
+            }
+            else{
                 statistics.incrementNoOfOtherEventTasks();
             }
@@ -255,10 +273,7 @@
      *
      */
-    private boolean isTextInput(ITask task) {
-        if (task.getInstances().size() > 0) {
-            ITaskInstance instance = task.getInstances().iterator().next();
-            if (instance instanceof IEventTaskInstance) {
-                return ((IEventTaskInstance) instance).getEvent().getType() instanceof TextInput;
-            }
+    private boolean isTextInput(ITaskInstance taskInstance) {
+        if (taskInstance instanceof IEventTaskInstance) {
+            return ((IEventTaskInstance) taskInstance).getEvent().getType() instanceof TextInput;
         }
 
@@ -269,28 +284,25 @@
      *
      */
-    private void calculateStatistics(IEventTask node, TextInputStatistics statistics) {
-        
-        for (ITaskInstance instance : node.getInstances()) {
-            IEventType type = ((IEventTaskInstance) instance).getEvent().getType();
-            IEventTarget target = ((IEventTaskInstance) instance).getEvent().getTarget();
-
-            if (type instanceof TextInput) {
-                String[] fragments =
-                    determineTextFragments(((TextInput) type).getEnteredText());
-                
-                if (target instanceof ITextField) {
-                    statistics.addTextFieldInput(node, fragments);
-                }
-                else if (target instanceof ITextArea) {
-                    statistics.addTextAreaInput(node, fragments);
-                }
-            }
-        }
-    }
-
-    /**
-     *
-     */
-    private String[] determineTextFragments(String enteredText) {
+    private void calculateStatistics(IEventTaskInstance  taskInstance,
+                                     IUserSession        session,
+                                     TextInputStatistics statistics)
+    {
+        IEventType type = taskInstance.getEvent().getType();
+        IEventTarget target = taskInstance.getEvent().getTarget();
+
+        if (type instanceof TextInput) {
+            if (target instanceof ITextField) {
+                statistics.addTextFieldInput(taskInstance, session);
+            }
+            else if (target instanceof ITextArea) {
+                statistics.addTextAreaInput(taskInstance, session);
+            }
+        }
+    }
+
+    /**
+     *
+     */
+/*    private static String[] determineTextFragments(String enteredText) {
         List<String> fragments = new ArrayList<String>();
 
@@ -304,5 +316,10 @@
                 // the previous fragment ended. so finalize it and start a new one
                 if ((fragment != null) && (fragment.length() > 0)) {
-                    fragments.add(fragment.toString());
+                    String fragmentStr = fragment.toString().trim();
+                    
+                    if (!"".equals(fragmentStr)) {
+                        fragments.add(fragmentStr);
+                    }
+                    
                     fragment = new StringBuffer();
                 }
@@ -314,5 +331,9 @@
 
         if ((fragment != null) && (fragment.length() > 0)) {
-            fragments.add(fragment.toString());
+            String fragmentStr = fragment.toString().trim();
+            
+            if (!"".equals(fragmentStr)) {
+                fragments.add(fragmentStr);
+            }
         }
 
@@ -323,5 +344,5 @@
      *
      */
-    private boolean isEqualCharacterType(char char1, char char2) {
+/*    private static boolean isEqualCharacterType(char char1, char char2) {
         return
             ((char1 == char2) ||
@@ -330,77 +351,245 @@
             (Character.isLetter(char1) && Character.isLetter(char2)) ||
             (Character.isJavaIdentifierPart(char1) && Character.isJavaIdentifierPart(char2)));
-    }
-
-    /**
-     * TODO comment
+    }*/
+
+    /**
+     *
+     */
+    private static class TextInputStatistics {
+        
+        /** */
+        private List<IEventTaskInstance> textFieldInputs = new ArrayList<IEventTaskInstance>();
+        
+        /** */
+        private Map<ITextField, List<String>> textFields = new HashMap<ITextField, List<String>>();
+
+        /** */
+        private List<IEventTaskInstance> textAreaInputs = new ArrayList<IEventTaskInstance>();
+
+        /** */
+        private Map<IUserSession, Map<String, TextEntryData>> textEntries =
+            new HashMap<IUserSession, Map<String, TextEntryData>>();
+
+        /** */
+        private int otherEventsCount;
+
+        /**
+         *
+         */
+        private void addTextFieldInput(IEventTaskInstance instance, IUserSession session) {
+            String enteredText = ((TextInput) instance.getEvent().getType()).getEnteredText();
+            
+            if ((enteredText != null) && (!"".equals(enteredText.trim()))) {
+                enteredText = enteredText.trim();
+                
+                textFieldInputs.add(instance);
+
+                // store text entries into text fields
+                List<String> entries = textFields.get(instance.getEvent().getTarget());
+
+                if (entries == null) {
+                    entries = new LinkedList<String>();
+                    textFields.put((ITextField) instance.getEvent().getTarget(), entries);
+                }
+                
+                entries.add(enteredText);
+
+                // writing down all text entries in text fields to check later for cooccurrences in
+                // same session
+                Map<String, TextEntryData> sessionTextEntries = textEntries.get(session);
+
+                if (sessionTextEntries == null) {
+                    sessionTextEntries = new HashMap<String, TextEntryData>();
+                    textEntries.put(session, sessionTextEntries);
+                }
+
+                TextEntryData data = sessionTextEntries.get(enteredText);
+
+                if (data == null) {
+                    data = new TextEntryData(enteredText);
+                    sessionTextEntries.put(enteredText, data);
+                }
+
+                data.addTaskInstance(instance);
+            }
+        }
+        
+        /**
+         *
+         */
+        public List<String> getAllInputsInto(ITextField textField) {
+            return textFields.get(textField);
+        }
+
+        /**
+         *
+         */
+        private int getUsageCount(ITextField textField) {
+            List<String> entries = textFields.get(textField);
+            
+            if (entries == null) {
+                return 0;
+            }
+            else {
+                return entries.size();
+            }
+        }
+
+        /**
+         *
+         */
+        private List<TextFieldCorrelation> determineTextFieldCorrelations() {
+            System.out.print("determining text field correlations of " + textFields.size() +
+                             " text fields ... ");
+            List<TextFieldCorrelation> correlations = new ArrayList<TextFieldCorrelation>();
+            
+            // we need an ordered list of text fields to be able compare all with each other
+            // through a nested loop
+            List<ITextField> textFieldList = getAllTextFields();
+            
+            List<TextEntryData> relevantTextEntryData = new LinkedList<TextEntryData>();
+            
+            for (Map<String, TextEntryData> sessionSpecEntries : textEntries.values()) {
+                for (TextEntryData data : sessionSpecEntries.values()) {
+                    if (data.textFields.size() > 1) {
+                        relevantTextEntryData.add(data);
+                    }
+                }
+            }
+            
+            for (int i = 0; i < (textFieldList.size() - 1); i++) {
+                for (int j = i + 1; j < textFieldList.size(); j++) {
+                    // count the number of times, in which the same text was entered in both
+                    // text fields within the same session
+                    List<String> sameEnteredTexts = new LinkedList<String>();
+
+                    for (TextEntryData data : relevantTextEntryData) {
+                        if (data.textFields.contains(textFieldList.get(i)) &&
+                            data.textFields.contains(textFieldList.get(j)))
+                        {
+                            sameEnteredTexts.add(data.enteredText);
+                        }
+                    }
+
+                    if (sameEnteredTexts.size() > 0) {
+                        // for the checked combination of text fields, there is at least once
+                        // the same text entered into both text fields during the same session
+                        correlations.add(new TextFieldCorrelation(textFieldList.get(i),
+                                                                  textFieldList.get(j),
+                                                                  sameEnteredTexts));
+                    }
+                }
+            }
+            
+            System.out.println("done");
+            
+            return correlations;
+        }
+
+        /**
+         *
+         */
+        private void addTextAreaInput(IEventTaskInstance instance, IUserSession session) {
+            textAreaInputs.add(instance);
+        }
+
+        /**
+         *
+         */
+        private int getNoOfAllEvents() {
+            return textFieldInputs.size() + textAreaInputs.size() + otherEventsCount;
+        }
+
+        /**
+         *
+         */
+        private int getNoOfTextFieldInputs() {
+            return textFieldInputs.size();
+        }
+
+        /**
+         *
+         */
+        private int getNoOfTextAreaInputs() {
+            return textAreaInputs.size();
+        }
+
+        /**
+         *
+         */
+        private void incrementNoOfOtherEventTasks() {
+            otherEventsCount++;
+        }
+
+        /**
+         * 
+         */
+        private List<ITextField> getAllTextFields() {
+            List<ITextField> textFieldList = new ArrayList<ITextField>(textFields.size());
+            
+            for (ITextField textField : textFields.keySet()) {
+                textFieldList.add(textField);
+            }
+            
+            return textFieldList;
+        }
+    }
+    
+    /**
      * 
-     * @version $Revision: $ $Date: 16.07.2012$
-     * @author 2012, last modified by $Author: pharms$
-     */
-    public static class TextInputStatistics {
-        
-        /** */
-        private List<Object[]> textFieldInputs = new ArrayList<Object[]>();
-
-        /** */
-        private List<Object[]> textAreaInputs = new ArrayList<Object[]>();
-
-        /** */
-        private int otherEventsCount;
-
-        /**
-         *
-         */
-        public void addTextFieldInput(IEventTask node, String[] fragments) {
-            textFieldInputs.add(new Object[] { node, fragments });
-        }
-
-        /**
-         *
-         */
-        public void addTextAreaInput(IEventTask node, String[] fragments) {
-            textAreaInputs.add(new Object[] { node, fragments });
-        }
-
-        /**
-         *
-         */
-        public int getNoOfAllEvents() {
-            return textFieldInputs.size() + textAreaInputs.size() + otherEventsCount;
-        }
-
-        /**
-         *
-         */
-        public int getNoOfTextFieldInputs() {
-            return textFieldInputs.size();
-        }
-
-        /**
-         *
-         */
-        public String[] getTextFieldInputFragments(int index) {
-            return (String[]) textFieldInputs.get(index)[1];
-        }
-
-        /**
-         *
-         */
-        public int getNoOfTextAreaInputs() {
-            return textAreaInputs.size();
-        }
-
-        /**
-         *
-         */
-        public String[] getTextAreaInputFragments(int index) {
-            return (String[]) textAreaInputs.get(index)[1];
-        }
-
-        /**
-         *
-         */
-        public void incrementNoOfOtherEventTasks() {
-            otherEventsCount++;
+     */
+    private static class TextEntryData {
+        
+        /** */
+        private String enteredText;
+        
+        /** */
+        private List<IEventTaskInstance> respectiveTaskInstances =
+            new LinkedList<IEventTaskInstance>();
+
+        /** */
+        private Set<ITextField> textFields = new HashSet<ITextField>();
+        
+        /**
+         *
+         */
+        private TextEntryData(String text) {
+            this.enteredText = text;
+        }
+
+        /**
+         *
+         */
+        private void addTaskInstance(IEventTaskInstance instance) {
+            respectiveTaskInstances.add(instance);
+            textFields.add((ITextField) instance.getEvent().getTarget());
+        }
+        
+    }
+
+    /**
+     * 
+     */
+    private static class TextFieldCorrelation {
+        
+        /** */
+        private List<String> enteredTexts = new LinkedList<String>();
+
+        /** */
+        private ITextField textField1;
+        
+        /** */
+        private ITextField textField2;
+        
+        /**
+         *
+         */
+        private TextFieldCorrelation(ITextField   textField1,
+                                     ITextField   textField2,
+                                     List<String> enteredTexts)
+        {
+            this.textField1 = textField1;
+            this.textField2 = textField2;
+            this.enteredTexts = enteredTexts;
         }
 
