Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CheckBoxMultipleSelectionRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CheckBoxMultipleSelectionRule.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CheckBoxMultipleSelectionRule.java	(revision 1918)
@@ -0,0 +1,245 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor;
+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.ITaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class CheckBoxMultipleSelectionRule implements UsabilityEvaluationRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
+     */
+    @Override
+    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
+        ValueChangeStatistics statistics = new ValueChangeStatistics();
+        
+        calculateStatistics(taskModel.getTasks(), statistics);
+
+        statistics.condenseCheckBoxGroups();
+        
+        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
+        analyzeStatistics(statistics, taskModel, results);
+
+        return results;
+    }
+
+    /**
+     *
+     */
+    private void analyzeStatistics(ValueChangeStatistics     statistics,
+                                   ITaskModel                taskModel,
+                                   UsabilityEvaluationResult results)
+    {
+        Map<IGUIElement, List<IGUIElement>> checkBoxGroups = statistics.getCheckBoxGroups();
+        
+        CHECK_NEXT_GROUP:
+        for (List<IGUIElement> group : checkBoxGroups.values()) {
+            Set<ITask> tasksUsingGroup = new HashSet<>();
+            
+            for (IGUIElement checkBox : group) {
+                Set<ITask> tasksUsingCheckBox = getTasksUsingCheckBox(checkBox, taskModel);
+                
+                for (ITask taskUsingCheckBox : tasksUsingCheckBox) {
+                    if (tasksUsingGroup.contains(taskUsingCheckBox)) {
+                        continue CHECK_NEXT_GROUP;
+                    }
+                    else {
+                        tasksUsingGroup.add(taskUsingCheckBox);
+                    }
+                }
+            }
+            
+            if (tasksUsingGroup.size() > 0) {
+                int eventCoverage = 0;
+                int allRecordedEvents = 0;
+                
+                for (ITask task : tasksUsingGroup) {
+                    if (task instanceof IEventTask) {
+                        eventCoverage +=
+                            taskModel.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE);
+                    }
+                }
+                
+                for (ITask task : taskModel.getTasks()) {
+                    if (task instanceof IEventTask) {
+                        allRecordedEvents +=
+                            taskModel.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE);
+                    }
+                }
+                
+                
+                UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity
+                    ((int) (1000 * eventCoverage / allRecordedEvents), eventCoverage, -1);
+                    
+                if (intensity != null) {
+                    Map<String, Object> parameters = new HashMap<String, Object>();
+                    parameters.put("radioButtons", group);
+
+                    results.addSmell
+                        (intensity, UsabilitySmellDescription.CHECK_BOX_SINGLE_SELECTION,
+                         parameters);
+                }
+            }
+        }
+    }
+
+    /**
+     * 
+     */
+    private Set<ITask> getTasksUsingCheckBox(final IGUIElement checkBox, ITaskModel taskModel) {
+        final Set<ITask> tasksUsingCheckBox = new HashSet<ITask>();
+        
+        for (ITask candidate : taskModel.getTasks()) {
+            candidate.accept(new DefaultTaskTraversingVisitor() {
+                @Override
+                public void visit(IEventTask eventTask) {
+                    if (!eventTask.getInstances().isEmpty()) {
+                        IEventTaskInstance instance =
+                            (IEventTaskInstance) eventTask.getInstances().iterator().next();
+                        
+                        if (checkBox.equals(instance.getEvent().getTarget())) {
+                            tasksUsingCheckBox.add(eventTask);
+                        }
+                    }
+                }
+                
+                @Override
+                public void visit(IStructuringTemporalRelationship relationship) {
+                    if (tasksUsingCheckBox.contains(relationship)) {
+                        return;
+                    }
+                    else {
+                        for (ITask child : relationship.getChildren()) {
+                            if (tasksUsingCheckBox.contains(child)) {
+                                tasksUsingCheckBox.add(relationship);
+                                return;
+                            }
+                        }
+                        
+                        super.visit(relationship);
+                        
+                        for (ITask child : relationship.getChildren()) {
+                            if (tasksUsingCheckBox.contains(child)) {
+                                tasksUsingCheckBox.add(relationship);
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                @Override
+                public void visit(IMarkingTemporalRelationship relationship) {
+                    if (tasksUsingCheckBox.contains(relationship)) {
+                        return;
+                    }
+                    else {
+                        if (tasksUsingCheckBox.contains(relationship.getMarkedTask())) {
+                            tasksUsingCheckBox.add(relationship);
+                            return;
+                        }
+                        
+                        super.visit(relationship);
+                        
+                        if (tasksUsingCheckBox.contains(relationship.getMarkedTask())) {
+                            tasksUsingCheckBox.add(relationship);
+                            return;
+                        }
+                    }
+                }
+            });
+        }
+        
+        return tasksUsingCheckBox;
+    }
+
+    /**
+     * 
+     */
+    private void calculateStatistics(Collection<ITask>     tasks,
+                                     ValueChangeStatistics statistics)
+    {
+        for (ITask task : tasks) {
+            if (task instanceof IEventTask) {
+                for (ITaskInstance taskInstance : task.getInstances()) {
+                    statistics.addValueChange((IEventTaskInstance) taskInstance);
+                }
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private static class ValueChangeStatistics {
+        
+        /** */
+        private Set<IGUIElement> checkBoxes = new HashSet<>();
+        
+        /** */
+        private Map<IGUIElement, List<IGUIElement>> checkBoxGroups = new HashMap<>();
+
+        /**
+         *
+         */
+        private void addValueChange(IEventTaskInstance instance) {
+            IGUIElement target = (IGUIElement) instance.getEvent().getTarget();
+            
+            if (target instanceof ICheckBox) {
+                checkBoxes.add(target);
+            }
+        }
+        
+        /**
+         *
+         */
+        private void condenseCheckBoxGroups() {
+            checkBoxGroups = RuleUtils.getGroups(checkBoxes, 3);
+        }
+
+        /**
+         *
+         */
+        private Map<IGUIElement, List<IGUIElement>> getCheckBoxGroups() {
+            return checkBoxGroups;
+        }
+
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CommonTaskRateRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CommonTaskRateRule.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CommonTaskRateRule.java	(revision 1918)
@@ -0,0 +1,136 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class CommonTaskRateRule implements UsabilityEvaluationRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
+     */
+    @Override
+    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
+        SummaryStatistics statistics = new SummaryStatistics();
+        int allObserved = calculateStatistics(taskModel.getUserSessions(), statistics);
+
+        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
+        analyzeStatistics(statistics, allObserved, results);
+
+        return results;
+    }
+
+    /**
+     *
+     */
+    private void analyzeStatistics(SummaryStatistics         statistics,
+                                   int                       allObserved,
+                                   UsabilityEvaluationResult results)
+    {
+        // the mean should tend to 0.1 (all subsequent 10 actions are covered by the same task).
+        // The ratio must be similar, i.e., if the mean is 0.1 the ratio is 0, if the mean is 1.0
+        // the ratio is 1000
+        
+        if (statistics.getN() > 0) {
+            double mean = statistics.getMean();
+            int ratio = (int) (10000 * (mean - 0.1) / 9);
+
+            UsabilitySmellIntensity intensity =
+                UsabilitySmellIntensity.getIntensity(ratio, allObserved, -1);
+
+            if (intensity != null) {
+                Map<String, Object> parameters = new HashMap<String, Object>();
+                parameters.put("ratio", new DecimalFormat("#.##").format(mean * 10));
+
+                results.addSmell(intensity, UsabilitySmellDescription.COMMON_TASK_RATE, parameters);
+            }
+        }
+    }
+
+
+    /**
+     * 
+     */
+    private int calculateStatistics(List<IUserSession>      sessions,
+                                    final SummaryStatistics statistics)
+    {
+        final LinkedList<ITaskInstance> rootNodes = new LinkedList<>();
+        final List<IEventTaskInstance> leafNodes = new ArrayList<>();
+        
+        for (IUserSession session : sessions) {
+            rootNodes.clear();
+            
+            for (final ITaskInstance currentRoot : session) {
+                currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() {
+                    @Override
+                    public void visit(IEventTaskInstance eventTaskInstance) {
+                        rootNodes.add(currentRoot);
+                        leafNodes.add(eventTaskInstance);
+
+                        if (rootNodes.size() >= 10) {
+                            statistics.addValue(getTaskCoverageMeasure(rootNodes));
+                            
+                            while (rootNodes.size() >= 10) {
+                                rootNodes.removeFirst();
+                            }
+                        }
+                    }
+                });
+            }
+        }
+        
+        return leafNodes.size();
+    }
+
+    /**
+     *
+     */
+    private double getTaskCoverageMeasure(LinkedList<ITaskInstance> rootNodes) {
+        int noOfDiffRoots = 0;
+        ITaskInstance prevRoot = null;
+        
+        for (ITaskInstance root : rootNodes) {
+            if (!root.equals(prevRoot)) {
+                noOfDiffRoots++;
+            }
+            
+            prevRoot = root;
+        }
+        
+        return (double) noOfDiffRoots / rootNodes.size();
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DataEntryMethodChangeRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DataEntryMethodChangeRule.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DataEntryMethodChangeRule.java	(revision 1918)
@@ -0,0 +1,190 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.eventcore.gui.KeyInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.eventcore.gui.TextSelection;
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class DataEntryMethodChangeRule implements UsabilityEvaluationRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
+     */
+    @Override
+    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
+        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
+
+        Map<ITask, Integer> smellingTasks = getDataEntryMethodChangeRatios(taskModel.getTasks());
+        analyzeDataEntryMethodChangeRatios(smellingTasks, results, taskModel);
+
+        return results;
+    }
+
+    /**
+     *
+     */
+    private void analyzeDataEntryMethodChangeRatios(Map<ITask, Integer>       smellingTasks,
+                                                    UsabilityEvaluationResult results,
+                                                    ITaskModel                taskModel)
+    {
+
+        for (Map.Entry<ITask, Integer> entry : smellingTasks.entrySet()) {
+            // data entry method change ratio of an instance is the sum of data entry method
+            // changes between subsequent events divided by the number of events minus one. The
+            // data entry method change ratio of a tasks is the average of the ratios of the
+            // instances. If this ratio is 0 no changes are done at all. If it is higher, it should
+            // be informed.
+            
+            UsabilitySmellIntensity intensity =
+                UsabilitySmellIntensity.getIntensity(entry.getValue(), entry.getKey(), taskModel);
+
+            if (intensity != null) {
+                Map<String, Object> parameters = new HashMap<String, Object>();
+
+                parameters.put("task", entry.getKey());
+                parameters.put("ratio", (float) entry.getValue() / 10);
+                
+                results.addSmell(entry.getKey(), intensity,
+                                 UsabilitySmellDescription.DATA_ENTRY_METHOD_CHANGE, parameters);
+            }
+        }
+    }
+
+    /**
+     * 
+     */
+    private Map<ITask, Integer> getDataEntryMethodChangeRatios(Collection<ITask> tasks) {
+        Map<ITask, Integer> impatienceRatios = new HashMap<ITask, Integer>();
+        
+        for (ITask task : tasks) {
+            if (task instanceof ISequence)  {
+                int ratio = getAverageDataEntryMethodChangeRatio((ISequence) task);
+                
+                if (ratio > 0) {
+                    impatienceRatios.put(task, ratio);
+                }
+            }
+        }
+        
+        return impatienceRatios;
+    }
+
+    /**
+     *
+     */
+    private int getAverageDataEntryMethodChangeRatio(ISequence task) {
+        if (task.getInstances().size() > 0) {
+            int cummulativeDataEntryMethodChangeRatio = 0;
+            for (ITaskInstance instance : task.getInstances()) {
+                cummulativeDataEntryMethodChangeRatio += getDataEntryMethodChangeRatio(instance);
+            }
+
+            return cummulativeDataEntryMethodChangeRatio / task.getInstances().size();
+        }
+        else {
+            return 0;
+        }
+    }
+
+    /**
+     *
+     */
+    private long getDataEntryMethodChangeRatio(ITaskInstance instance) {
+        final List<IEventTaskInstance> terminalNodes = new LinkedList<>();
+        
+        instance.accept(new DefaultTaskInstanceTraversingVisitor() {
+            @Override
+            public void visit(IEventTaskInstance eventTaskInstance) {
+                terminalNodes.add(eventTaskInstance);
+            }
+        });
+        
+        if (terminalNodes.size() > 1) {
+            IEventTaskInstance previous = null;
+            int changeCount = 0;
+
+            for (IEventTaskInstance current : terminalNodes) {
+                if (previous == null) {
+                    previous = current;
+                }
+                else if (dataEntryMethodChanges(previous, current)) {
+                    changeCount++;
+                }
+            }
+
+            return changeCount * 1000 / (terminalNodes.size() - 1);
+        }
+        else {
+            return 0;
+        }
+    }
+
+
+    /**
+     *
+     */
+    private boolean dataEntryMethodChanges(IEventTaskInstance first, IEventTaskInstance second) {
+        IEventType firstEventType = first.getEvent().getType();
+        IEventType secondEventType = second.getEvent().getType();
+        
+        boolean hasKeyboardInteraction =
+            isKeyboardInteraction(firstEventType) || isKeyboardInteraction(secondEventType);
+        
+        boolean hasMouseInteraction =
+            isMouseInteraction(firstEventType) || isMouseInteraction(secondEventType);
+        
+        return hasKeyboardInteraction && hasMouseInteraction;
+    }
+
+    /**
+     *
+     */
+    private boolean isKeyboardInteraction(IEventType eventType) {
+        return (eventType instanceof KeyInteraction) || (eventType instanceof TextInput);
+    }
+
+    /**
+     *
+     */
+    private boolean isMouseInteraction(IEventType eventType) {
+        return (eventType instanceof MouseInteraction) || (eventType instanceof ValueSelection) ||
+            (eventType instanceof TextSelection) || (eventType instanceof Scroll);
+    }
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultCursorPositioningRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultCursorPositioningRule.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultCursorPositioningRule.java	(revision 1918)
@@ -0,0 +1,226 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.math3.stat.inference.ChiSquareTest;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IEventTarget;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class DefaultCursorPositioningRule implements UsabilityEvaluationRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
+     */
+    @Override
+    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
+        FirstViewActionStatistics statistics = new FirstViewActionStatistics();
+        calculateStatistics(taskModel.getUserSessions(), statistics);
+
+        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
+        analyzeStatistics(statistics, results);
+
+        return results;
+    }
+
+    /**
+     *
+     */
+    private void analyzeStatistics(FirstViewActionStatistics statistics,
+                                   UsabilityEvaluationResult results)
+    {
+        for (Map.Entry<IGUIView, Map<IEventTask, Integer>> firstActions :
+                statistics.getFirstViewActions().entrySet())
+        {
+            long[] observed = new long[firstActions.getValue().size()];
+            long allObservedInView = 0;
+            long maxObserved = 0;
+            int i = 0;
+            IEventTask mostOftenDoneFirst = null;
+            
+            for (Map.Entry<IEventTask, Integer> firstAction : firstActions.getValue().entrySet()) {
+                observed[i++] = firstAction.getValue();
+                allObservedInView += firstAction.getValue();
+                
+                if (maxObserved < firstAction.getValue()) {
+                    maxObserved = firstAction.getValue();
+                    mostOftenDoneFirst = firstAction.getKey();
+                }
+            }
+            
+            double[] expected = new double[firstActions.getValue().size()];
+            double expectedFrequency =
+                ((double) statistics.getViewOpenedCount(firstActions.getKey())) / expected.length;
+            
+            for (i = 0; i < expected.length; i++) {
+                expected[i] = expectedFrequency;
+            }
+            
+            if ((allObservedInView > 1) &&
+                ((expected.length == 1) ||
+                 (new ChiSquareTest().chiSquareTest(expected, observed, 0.05))))
+            {
+                // values are not equally distributed.
+                    
+                UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity
+                    ((int) (1000 * maxObserved / allObservedInView), (int) allObservedInView, -1);
+                        
+                if ((intensity != null) && isCursorPositioningAction(mostOftenDoneFirst)) {
+                    Map<String, Object> parameters = new HashMap<String, Object>();
+                    parameters.put("view", firstActions.getKey());
+                    parameters.put("textfield", getTarget(mostOftenDoneFirst));
+                    parameters.put("ratio", new DecimalFormat("#.##").format
+                                       (100.0 * maxObserved / allObservedInView));
+
+                    results.addSmell(intensity, UsabilitySmellDescription.MOST_OFTEN_DONE_FIRST,
+                                     parameters);
+                }
+            }
+        }
+    }
+
+
+    /**
+     *
+     */
+    private boolean isCursorPositioningAction(IEventTask task) {
+        Event event = ((IEventTaskInstance) task.getInstances().iterator().next()).getEvent();
+        return ((event.getType() instanceof MouseClick) ||
+                (event.getType() instanceof MouseButtonDown)) &&
+               (((MouseButtonInteraction) event.getType()).getButton() ==
+                   MouseButtonInteraction.Button.LEFT) &&
+               ((event.getTarget() instanceof ITextField) ||
+                (event.getTarget() instanceof ITextArea));
+    }
+
+    /**
+     *
+     */
+    private IEventTarget getTarget(IEventTask task) {
+        return ((IEventTaskInstance) task.getInstances().iterator().next()).getEvent().getTarget();
+    }
+
+    /**
+     * 
+     */
+    private void calculateStatistics(List<IUserSession>              sessions,
+                                     final FirstViewActionStatistics statistics)
+    {
+        final IGUIView[] currentView = new IGUIView[1];
+        
+        for (IUserSession session : sessions) {
+            for (final ITaskInstance currentRoot : session) {
+                currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() {
+                    @Override
+                    public void visit(IEventTaskInstance eventTaskInstance) {
+                        // ignore scrolling to get something semantically helpful
+                        if ((eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) &&
+                            (!(eventTaskInstance.getEvent().getType() instanceof Scroll)))
+                        {
+                            IGUIView view =
+                                ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView();
+                            
+                            if (((currentView[0] == null) && (view != null)) ||
+                                ((currentView[0] != null) && (!currentView[0].equals(view))))
+                            {
+                                currentView[0] = view;
+                                statistics.addFirstViewAction
+                                    (view, eventTaskInstance.getEventTask());
+                            }
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private class FirstViewActionStatistics {
+        
+        /** */
+        private Map<IGUIView, Map<IEventTask, Integer>> firstViewActions = new HashMap<>();
+        
+        /**
+         *
+         */
+        private void addFirstViewAction(IGUIView view, IEventTask action) {
+            Map<IEventTask, Integer> counterMap = firstViewActions.get(view);
+            
+            if (counterMap == null) {
+                counterMap = new HashMap<>();
+                firstViewActions.put(view, counterMap);
+            }
+            
+            Integer counter = counterMap.get(action);
+            
+            if (counter == null) {
+                counterMap.put(action, 1);
+            }
+            else {
+                counterMap.put(action, counter + 1);
+            }
+        }
+
+        /**
+         *
+         */
+        private Map<IGUIView, Map<IEventTask, Integer>> getFirstViewActions() {
+            return firstViewActions;
+        }
+
+        /**
+         *
+         */
+        private int getViewOpenedCount(IGUIView view) {
+            Map<IEventTask, Integer> counterMap = firstViewActions.get(view);
+            int count = 0;
+            
+            for (Integer counter : counterMap.values()) {
+                count += counter;
+            }
+            
+            return count;
+        }
+    }
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultValueRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultValueRule.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultValueRule.java	(revision 1918)
@@ -0,0 +1,696 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.math3.stat.inference.ChiSquareTest;
+
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
+import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IRadioButton;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class DefaultValueRule implements UsabilityEvaluationRule {
+    
+    /** */
+    private static String DEFAULT_VALUE = "DEFAULT_VALUE";
+    
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
+     */
+    @Override
+    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
+        System.out.println("determining value selection targets");
+        Set<ValueSelectionTarget> targets = getValueSelectionTargets(taskModel.getTasks());
+        System.out.println("found " + targets.size() + " targets");
+        
+        System.out.println("grouping radio buttons and check boxes targets");
+        condenseRadioButtonAndCheckBoxGroups(targets);
+        System.out.println(targets.size() + " remaining");
+        
+        System.out.println("calculating statistics");
+        ValueChangeStatistics statistics = new ValueChangeStatistics(targets);
+        calculateStatistics(taskModel.getUserSessions(), statistics);
+
+        System.out.println("analyzing statistics");
+        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
+        analyzeStatistics(statistics, results);
+
+        return results;
+    }
+
+    /**
+     *
+     */
+    private void analyzeStatistics(ValueChangeStatistics     statistics,
+                                   UsabilityEvaluationResult results)
+    {
+        System.out.println
+            ("determined " + statistics.getViewsWithValueSelections().size() + " views");
+        
+        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+        int valueSelectionTargetCounter = 0;
+        
+        for (IGUIView view : statistics.getViewsWithValueSelections()) {
+            valueSelectionTargetCounter += statistics.getValueSelectionTargetsInView(view).size();
+        }
+        
+        if (valueSelectionTargetCounter != statistics.selectedValues.size()) {
+            throw new IllegalStateException("this should not happen");
+        }
+        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+        
+        for (IGUIView view : statistics.getViewsWithValueSelections()) {
+            //System.out.println("view " + view + " has " +
+            //                   statistics.getValueSelectionTargetsInView(view) +
+            //                   " value selection targets");
+            for (ValueSelectionTarget target : statistics.getValueSelectionTargetsInView(view)) {
+                analyzeStatistics
+                    (view, target, statistics.getStatisticsForValueSelectionTarget(target), results);
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private void analyzeStatistics(IGUIView                  view,
+                                   ValueSelectionTarget      target,
+                                   Map<Object, Integer>      selectedValues,
+                                   UsabilityEvaluationResult results)
+    {
+        //System.out.println("  analyzing selected values for " + target);
+        long[] observed = new long[selectedValues.size()];
+        long allObserved = 0;
+        long maxObserved = 0;
+        LinkedList<Object> mostOftenSelected = new LinkedList<>();
+        
+        int i = 0;
+        for (Map.Entry<Object, Integer> selectedValue : selectedValues.entrySet()) {
+            //System.out.println("    " + selectedValue.getValue() + " \t " + selectedValue.getKey());
+            
+            observed[i++] = selectedValue.getValue();
+            allObserved += selectedValue.getValue();
+            maxObserved = Math.max(maxObserved, selectedValue.getValue());
+            
+            if (mostOftenSelected.size() > 0) {
+                ListIterator<Object> iterator = mostOftenSelected.listIterator();
+                while (iterator.hasNext()) {
+                    if (selectedValues.get(iterator.next()) < selectedValue.getValue()) {
+                        iterator.previous();
+                        iterator.add(selectedValue.getKey());
+                    
+                        while (mostOftenSelected.size() > 5) {
+                            mostOftenSelected.removeLast();
+                        }
+                    
+                        break;
+                    }
+                }
+            }
+            else {
+                mostOftenSelected.add(selectedValue.getKey());
+            }
+        }
+        
+        double[] expected = new double[observed.length];
+        double expectedFrequency = ((double) allObserved) / expected.length;
+        
+        for (i = 0; i < expected.length; i++) {
+            expected[i] = expectedFrequency;
+        }
+        
+        if ((expected.length == 1) ||
+            (new ChiSquareTest().chiSquareTest(expected, observed, 0.05)))
+        {
+            // values are not equally distributed.
+            
+            // if the default value is most often selected, everything is fine. If not, smell is
+            // detected
+            if (!DEFAULT_VALUE.equals(mostOftenSelected.get(0))) {
+                UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity
+                    ((int) (1000 * maxObserved / allObserved), (int) allObserved, -1);
+                
+                if (intensity != null) {
+                    List<String> mostOftenSelectedValues =
+                        new ArrayList<>(mostOftenSelected.size());
+                
+                    for (Object oftenSelected : mostOftenSelected) {
+                        mostOftenSelectedValues.add
+                            (oftenSelected + " (" +
+                             (100.0 * selectedValues.get(oftenSelected) / allObserved) + "%)");
+                    }
+                
+                    Map<String, Object> parameters = new HashMap<String, Object>();
+                    parameters.put("view", view);
+                    parameters.put("guiElement", target);
+                    parameters.put("selectedValues", mostOftenSelectedValues);
+
+                    results.addSmell(intensity, UsabilitySmellDescription.GOOD_DEFAULTS, parameters);
+                }
+            }
+        }
+    }
+
+    /**
+     * 
+     */
+    private Set<ValueSelectionTarget> getValueSelectionTargets(Collection<ITask> tasks) {
+        Set<ValueSelectionTarget> result = new HashSet<>();
+        
+        for (ITask task : tasks) {
+            if (task instanceof IEventTask) {
+                for (ITaskInstance instance : task.getInstances()) {
+                    if (isValueSelection(instance)) {
+                        result.add(newValueSelectionTarget(instance));
+                    }
+                }
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     *
+     */
+    private boolean isValueSelection(ITaskInstance instance) {
+        return (instance instanceof IEventTaskInstance) &&
+            ((((IEventTaskInstance) instance).getEvent().getType() instanceof TextInput) ||
+             (((IEventTaskInstance) instance).getEvent().getType() instanceof ValueSelection));
+    }
+
+    /**
+     *
+     */
+    private static ValueSelectionTarget newValueSelectionTarget(ITaskInstance instance) {
+        Event event = ((IEventTaskInstance) instance).getEvent();
+        return new ValueSelectionTarget((IGUIElement) event.getTarget());
+    }
+    
+    /**
+     *
+     */
+    private void condenseRadioButtonAndCheckBoxGroups(Set<ValueSelectionTarget> targets) {
+        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+        Set<IGUIView> viewsBefore = new HashSet<IGUIView>();
+        
+        for (ValueSelectionTarget target : targets) {
+            viewsBefore.add(target.getView());
+        }
+        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+        
+        condenseGuiElementGroups(targets, IRadioButton.class);
+        condenseGuiElementGroups(targets, ICheckBox.class);
+        
+        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+        Set<IGUIView> viewsAfter = new HashSet<IGUIView>();
+        
+        for (ValueSelectionTarget target : targets) {
+            viewsAfter.add(target.getView());
+        }
+        
+        if (viewsBefore.size() != viewsAfter.size()) {
+            throw new IllegalStateException("this should not happen");
+        }
+        
+        for (IGUIView view : viewsBefore) {
+            if (!viewsAfter.contains(view)) {
+                throw new IllegalStateException("this should not happen too");
+            }
+        }
+        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+    }
+    
+    /**
+     *
+     */
+    private void condenseGuiElementGroups(Set<ValueSelectionTarget> targets, Class<?> type) {
+        Map<IGUIView, List<IGUIElement>> guiElementsInViews = new HashMap<>();
+        
+        // determine the targets under consideration
+        for (ValueSelectionTarget target : targets) {
+            if (type.isInstance(target.getTarget())) {
+                List<IGUIElement> guiElementsInView = guiElementsInViews.get(target.getView());
+
+                if (guiElementsInView == null) {
+                    guiElementsInView = new LinkedList<IGUIElement>();
+                    guiElementsInViews.put(target.getView(), guiElementsInView);
+                }
+                
+                guiElementsInView.add(target);
+            }
+        }
+
+        for (Map.Entry<IGUIView, List<IGUIElement>> guiElements :
+                 guiElementsInViews.entrySet())
+        {
+            Map<IGUIElement, List<IGUIElement>> groups =
+                RuleUtils.getGroups(guiElements.getValue(), 3);
+
+            for (Map.Entry<IGUIElement, List<IGUIElement>> group : groups.entrySet()) {
+                //System.out.println("replacing");
+
+                // it is important to provide the correct view here, as the view of group.getKey()
+                // for HTML stuff may not be correct due to GUI model condensing
+                ValueSelectionTargetGroup valueSelectionTargetGroup =
+                    new ValueSelectionTargetGroup(group.getKey(), guiElements.getKey());
+                
+                for (IGUIElement toBeMerged : group.getValue()) {
+                    //System.out.println(targets.size() + "  " + toBeMerged.getView() + "  " +
+                    //                   toBeMerged);
+                    
+                    valueSelectionTargetGroup.addTargetToGroup(toBeMerged);
+                    targets.remove(toBeMerged);
+                }
+                
+                //System.out.println("with " + valueSelectionTargetGroup.getView() + "  " +
+                //                   valueSelectionTargetGroup);
+                targets.add(valueSelectionTargetGroup);
+            }
+        }
+    }
+
+    /**
+     * 
+     */
+    private void calculateStatistics(List<IUserSession>          sessions,
+                                     final ValueChangeStatistics statistics)
+    {
+        final IGUIView[] currentView = new IGUIView[1];
+        final List<IEventTaskInstance> valueChangesInViewDisplay = new ArrayList<>();
+        
+        for (IUserSession session : sessions) {
+            currentView[0] = null;
+            valueChangesInViewDisplay.clear();
+            
+            for (final ITaskInstance currentRoot : session) {
+                currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() {
+                    @Override
+                    public void visit(IEventTaskInstance eventTaskInstance) {
+                        if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) {
+                            IGUIView view =
+                                ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView();
+                            
+                            if ((currentView[0] == null) && (view != null)) {
+                                currentView[0] = view;
+                                valueChangesInViewDisplay.clear();
+                            }
+                            else if ((currentView[0] != null) && (!currentView[0].equals(view))) {
+                                statistics.addValueChangesInViewDisplay
+                                    (currentView[0], valueChangesInViewDisplay);
+                                
+                                currentView[0] = view;
+                                valueChangesInViewDisplay.clear();
+                            }
+                        }
+                        
+                        if (isValueSelection(eventTaskInstance)) {
+                            valueChangesInViewDisplay.add(eventTaskInstance);
+                        }
+                    }
+                });
+            }
+            
+            // add the selected values of the last shown view in the session
+            if (currentView[0] != null) {
+                statistics.addValueChangesInViewDisplay(currentView[0], valueChangesInViewDisplay);
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private static class ValueChangeStatistics {
+        
+        /** */
+        private Map<ValueSelectionTarget, ValueSelectionTarget> targetReplacements =
+            new HashMap<>();
+        
+        /** */
+        private Map<IGUIView, List<ValueSelectionTarget>> valueSelectionTargetsInView =
+            new HashMap<>();
+        
+        /** */
+        private Map<ValueSelectionTarget, Map<Object, Integer>> selectedValues = new HashMap<>();
+
+        /**
+         *
+         */
+        public ValueChangeStatistics(Set<ValueSelectionTarget> targets) {
+            for (ValueSelectionTarget target : targets) {
+                if (target instanceof ValueSelectionTargetGroup) {
+                    for (IGUIElement groupElement : (ValueSelectionTargetGroup) target) {
+                        targetReplacements.put((ValueSelectionTarget) groupElement, target);
+                    }
+                }
+                else {
+                    targetReplacements.put((ValueSelectionTarget) target, target);
+                }
+                
+                List<ValueSelectionTarget> targetsInView =
+                    valueSelectionTargetsInView.get(target.getView());
+                
+                if (targetsInView == null) {
+                    targetsInView = new LinkedList<ValueSelectionTarget>();
+                    valueSelectionTargetsInView.put(target.getView(), targetsInView);
+                }
+                
+                targetsInView.add((ValueSelectionTarget) target);
+            }
+        }
+
+        /**
+         *
+         */
+        private void addValueChangesInViewDisplay(IGUIView                 view,
+                                                  List<IEventTaskInstance> valueChanges)
+        {
+            if (!valueSelectionTargetsInView.containsKey(view)) {
+                if (!valueChanges.isEmpty()) {
+                    throw new IllegalStateException("this should not happen, as we already " +
+                                                    "recorded the views with possible value " +
+                                                    "changes at this point");
+                }
+                
+                // view does not contain value changes --> return 
+                return;
+            }
+            
+            //System.out.println
+            //    ("handling " + valueChanges.size() + " value changes on view " + view);
+        
+            Map<ValueSelectionTarget, Object> lastSelectedValues = new HashMap<>();
+            
+            // determine the last selected value for each of the value selection targets that were
+            // actively selected by the user
+            for (IEventTaskInstance valueChange : valueChanges) {
+                ValueSelectionTarget target = newValueSelectionTarget(valueChange);
+                Object selectedValue;
+                
+                if (valueChange.getEvent().getType() instanceof TextInput) {
+                    selectedValue = ((TextInput) valueChange.getEvent().getType()).getEnteredText();
+                }
+                else if (valueChange.getEvent().getType() instanceof ValueSelection) {
+                    selectedValue =
+                        ((ValueSelection<?>) valueChange.getEvent().getType()).getSelectedValue();
+                    
+                    if ((target instanceof IRadioButton) || (target instanceof ICheckBox)) {
+                        selectedValue = selectedValue + " (" + target + ")";
+                    }
+                }
+                else {
+                    throw new IllegalStateException("the implementation needs to be extended to " +
+                                                    "handle further value change types");
+                }
+                
+                target = targetReplacements.get(target);
+                lastSelectedValues.put(target, selectedValue);
+            }
+            
+            // add the default value selection for the unchanged value selection targets
+            for (ValueSelectionTarget target : valueSelectionTargetsInView.get(view)) {
+                if (!lastSelectedValues.containsKey(target)) {
+                    lastSelectedValues.put(target, DEFAULT_VALUE);
+                }
+            }
+            
+            // and now store the statistics
+            for (Map.Entry<ValueSelectionTarget, Object> selectedValue :
+                     lastSelectedValues.entrySet())
+            {
+                Map<Object, Integer> statistics = selectedValues.get(selectedValue.getKey());
+                
+                if (statistics == null) {
+                    statistics = new HashMap<>();
+                    selectedValues.put(selectedValue.getKey(), statistics);
+                }
+                
+                Integer counter = statistics.get(selectedValue.getValue());
+                
+                if (counter == null) {
+                    statistics.put(selectedValue.getValue(), 1);
+                }
+                else {
+                    statistics.put(selectedValue.getValue(), counter + 1);
+                }
+            }
+        }
+
+        /**
+         * 
+         */
+        private Collection<IGUIView> getViewsWithValueSelections() {
+            return valueSelectionTargetsInView.keySet();
+        }
+
+        /**
+         * 
+         */
+        private Collection<ValueSelectionTarget> getValueSelectionTargetsInView(IGUIView view) {
+            return valueSelectionTargetsInView.get(view);
+        }
+
+        /**
+         * 
+         */
+        private Map<Object, Integer> getStatisticsForValueSelectionTarget
+            (ValueSelectionTarget target)
+        {
+            return selectedValues.get(target);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class ValueSelectionTarget implements IGUIElement {
+
+        /**  */
+        private static final long serialVersionUID = 1L;
+
+        /** */
+        private IGUIView view;
+        
+        /** */
+        private IGUIElement target;
+        
+        /**
+         *
+         */
+        private ValueSelectionTarget(IGUIElement target) {
+            this.view = target.getView();
+            this.target = target;
+        }
+        
+        /**
+         *
+         */
+        private ValueSelectionTarget(IGUIElement target, IGUIView view) {
+            this.view = view;
+            this.target = target;
+        }
+
+        /**
+         *
+         */
+        public IGUIElementSpec getSpecification() {
+            return target.getSpecification();
+        }
+
+        /**
+         *
+         */
+        public String getPlatform() {
+            return target.getPlatform();
+        }
+
+        /**
+         *
+         */
+        public IGUIElement getParent() {
+            return target.getParent();
+        }
+
+        /**
+         *
+         */
+        public String getStringIdentifier() {
+            return target.getStringIdentifier();
+        }
+
+        /**
+         *
+         */
+        public GUIModel getGUIModel() {
+            return target.getGUIModel();
+        }
+
+        /**
+         *
+         */
+        public boolean isUsed() {
+            return target.isUsed();
+        }
+
+        /**
+         *
+         */
+        public void markUsed() {
+            target.markUsed();
+        }
+
+        /**
+         *
+         */
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            else if (obj instanceof ValueSelectionTarget) {
+                ValueSelectionTarget other = (ValueSelectionTarget) obj;
+                return (other.view.equals(this.view) && other.target.equals(this.target));
+            }
+            
+            return false;
+        }
+
+        /**
+         *
+         */
+        public int hashCode() {
+            return view.hashCode();
+        }
+
+        /**
+         *
+         */
+        public void updateSpecification(IGUIElementSpec furtherSpec) {
+            target.updateSpecification(furtherSpec);
+        }
+
+        /**
+         * 
+         */
+        public void addEqualGUIElement(IGUIElement equalElement) {
+            target.addEqualGUIElement(equalElement);
+        }
+
+        /**
+         *
+         */
+        public double getDistanceTo(IGUIElement otherElement) {
+            return target.getDistanceTo(otherElement);
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return target.toString();
+        }
+
+        /**
+         *
+         */
+        public IGUIView getView() {
+            return view;
+        }
+
+        /**
+         *
+         */
+        private IGUIElement getTarget() {
+            return target;
+        }
+
+    }
+
+    /**
+     *
+     */
+    private static class ValueSelectionTargetGroup extends ValueSelectionTarget
+        implements Iterable<IGUIElement>
+    {
+
+        /**  */
+        private static final long serialVersionUID = 1L;
+        
+        /** */
+        private List<IGUIElement> groupedTargets = new LinkedList<>();
+        
+        /**
+         *
+         */
+        private ValueSelectionTargetGroup(IGUIElement target, IGUIView view) {
+            super(target, view);
+        }
+
+        /**
+         * 
+         */
+        private void addTargetToGroup(IGUIElement target) {
+            groupedTargets.add(target);
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Iterable#iterator()
+         */
+        @Override
+        public Iterator<IGUIElement> iterator() {
+            return groupedTargets.iterator();
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return "group(" + groupedTargets.size() + " targets, view " + super.getView() + ")";
+        }
+    }
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/EventCoverageQuantileRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/EventCoverageQuantileRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/EventCoverageQuantileRule.java	(revision 1918)
@@ -57,6 +57,6 @@
 
                 //TODO document magic numbers
-                UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-                    (ratio, 990, 975, 950, 900, sequence, taskModel);
+                UsabilitySmellIntensity severity =
+                    UsabilitySmellIntensity.getIntensity(ratio, sequence, taskModel);
 
                 if (severity != null) {
@@ -81,6 +81,6 @@
                     parameters.put("ratio", effRatio);
 
-                    results.addDefect(severity, UsabilityDefectDescription.HIGH_EVENT_COVERAGE,
-                                      parameters);
+                    results.addSmell(sequence, severity,
+                                     UsabilitySmellDescription.HIGH_EVENT_COVERAGE, parameters);
                 }
             }
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/EventCoverageRatioRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/EventCoverageRatioRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/EventCoverageRatioRule.java	(revision 1918)
@@ -56,7 +56,6 @@
                     (TaskMetric.EVENT_COVERAGE_RATIO);
 
-                //TODO document magic numbers
-                UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-                    (ratio, 300, 150, 50, 10, sequence, taskModel);
+                UsabilitySmellIntensity severity =
+                    UsabilitySmellIntensity.getIntensity(ratio, sequence, taskModel);
 
                 if (severity != null) {
@@ -65,6 +64,6 @@
                     parameters.put("ratio", (ratio / 10));
 
-                    results.addDefect(severity, UsabilityDefectDescription.HIGH_EVENT_COVERAGE,
-                                      parameters);
+                    results.addSmell(sequence, severity,
+                                     UsabilitySmellDescription.HIGH_EVENT_COVERAGE, parameters);
                 }
             }
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MisleadingClickCueRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MisleadingClickCueRule.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MisleadingClickCueRule.java	(revision 1918)
@@ -0,0 +1,241 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.eventcore.IEventType;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
+import de.ugoe.cs.autoquest.eventcore.gui.MouseDoubleClick;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IImage;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IText;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class MisleadingClickCueRule implements UsabilityEvaluationRule {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
+     */
+    @Override
+    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
+        UselessClickStatistics statistics = new UselessClickStatistics();
+        int allObserved = calculateStatistics(taskModel.getUserSessions(), statistics);
+
+        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
+        analyzeStatistics(statistics, allObserved, results);
+
+        return results;
+    }
+
+    /**
+     *
+     */
+    private void analyzeStatistics(UselessClickStatistics    statistics,
+                                   int                       allObserved,
+                                   UsabilityEvaluationResult results)
+    {
+        for (Map.Entry<IGUIView, Map<IGUIElement, Integer>> uselessClickCounter :
+                statistics.getUselessClickCounters().entrySet())
+        {
+            for (Map.Entry<IGUIElement, Integer> counter : uselessClickCounter.getValue().entrySet())
+            {
+                int uselessClicks = counter.getValue();
+                int noOfViewDisplays = statistics.getViewOpenedCount(uselessClickCounter.getKey());
+
+                int ratio = Math.min(1000, 1000 * uselessClicks / noOfViewDisplays);
+
+                UsabilitySmellIntensity intensity =
+                    UsabilitySmellIntensity.getIntensity(ratio, uselessClicks, -1);
+
+                if (intensity != null) {
+                    Map<String, Object> parameters = new HashMap<String, Object>();
+                    parameters.put("noOfViewDisplays", noOfViewDisplays);
+                    parameters.put("uselessClicks", uselessClicks);
+                    parameters.put("element", counter.getKey());
+                    parameters.put("view", uselessClickCounter.getKey());
+
+                    results.addSmell
+                        (intensity, UsabilitySmellDescription.MISLEADING_CLICK_CUE, parameters);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 
+     */
+    private int calculateStatistics(List<IUserSession>            sessions,
+                                     final UselessClickStatistics statistics)
+    {
+        final List<IEventTaskInstance> leafNodes = new ArrayList<>();
+        final IGUIView[] currentView = new IGUIView[1];
+        
+        for (IUserSession session : sessions) {
+            currentView[0] = null;
+            
+            for (final ITaskInstance currentRoot : session) {
+                currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() {
+                    @Override
+                    public void visit(IEventTaskInstance eventTaskInstance) {
+                        leafNodes.add(eventTaskInstance);
+                        
+                        if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) {
+                            IGUIView view =
+                                ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView();
+                            
+                            if (((currentView[0] == null) && (view != null)) ||
+                                ((currentView[0] != null) && (!currentView[0].equals(view))))
+                            {
+                                currentView[0] = view;
+                                statistics.addViewOpened(view);
+                            }
+                            
+                            if (isUselessMouseClick(eventTaskInstance)) {
+                                statistics.addUselessClick(eventTaskInstance);
+                            }
+                        }
+                    }
+                });
+            }
+        }
+        
+        return leafNodes.size();
+    }
+
+    /**
+     *
+     */
+    private boolean isUselessMouseClick(IEventTaskInstance eventTaskInstance) {
+        IEventType type = eventTaskInstance.getEvent().getType();
+        
+        if ((!(type instanceof MouseClick)) && (!(type instanceof MouseDoubleClick))) {
+            return false;
+        }
+        
+        if (((MouseButtonInteraction) type).getButton() != MouseButtonInteraction.Button.LEFT) {
+            return false;
+        }
+                
+        IGUIElement target = (IGUIElement) eventTaskInstance.getEvent().getTarget();
+        
+        if ((target instanceof IImage) /*|| (target instanceof IPanel) ||
+            (target instanceof IProgressBar) || (target instanceof IScrollPane) ||
+            (target instanceof IShape) || (target instanceof ITable)*/ ||
+            (target instanceof IText))
+        {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     *
+     */
+    private class UselessClickStatistics {
+        
+        /** */
+        private Map<IGUIView, Integer> viewOpenedCounters = new HashMap<>();
+        
+        /** */
+        private Map<IGUIView, Map<IGUIElement, Integer>> uselessClickCounters = new HashMap<>();
+
+        /**
+         * <p>
+         * TODO: comment
+         * </p>
+         *
+         * @param view
+         */
+        private void addViewOpened(IGUIView view) {
+            Integer counter = viewOpenedCounters.get(view);
+            
+            if (counter == null) {
+                viewOpenedCounters.put(view, 1);
+            }
+            else {
+                viewOpenedCounters.put(view, counter + 1);
+            }
+        }
+
+        /**
+         * <p>
+         * TODO: comment
+         * </p>
+         *
+         * @param eventTaskInstance
+         */
+        private void addUselessClick(IEventTaskInstance eventTaskInstance) {
+            Map<IGUIElement, Integer> counterMap = uselessClickCounters.get
+                (((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView());
+            
+            if (counterMap == null) {
+                counterMap = new HashMap<>();
+                uselessClickCounters.put
+                    (((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView(), counterMap);
+            }
+            
+            Integer counter = counterMap.get(eventTaskInstance.getEvent().getTarget());
+            
+            if (counter == null) {
+                counterMap.put((IGUIElement) eventTaskInstance.getEvent().getTarget(), 1);
+            }
+            else {
+                counterMap.put((IGUIElement) eventTaskInstance.getEvent().getTarget(), counter + 1);
+            }
+        }
+
+        /**
+         *
+         */
+        private Map<IGUIView, Map<IGUIElement, Integer>> getUselessClickCounters() {
+            return uselessClickCounters;
+        }
+
+        /**
+         *
+         */
+        private int getViewOpenedCount(IGUIView view) {
+            Integer counter = viewOpenedCounters.get(view);
+            
+            if (counter == null) {
+                return 0;
+            }
+            else {
+                return counter;
+            }
+        }
+    }
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MissingFeedbackRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MissingFeedbackRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MissingFeedbackRule.java	(revision 1918)
@@ -78,8 +78,8 @@
             // be considered as usability smell.
             
-            UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-                (entry.getValue(), 2000, 1000, 500, 50, entry.getKey(), taskModel);
-
-            if (severity != null) {
+            UsabilitySmellIntensity intensity =
+                UsabilitySmellIntensity.getIntensity(entry.getValue(), entry.getKey(), taskModel);
+
+            if (intensity != null) {
                 Map<String, Object> parameters = new HashMap<String, Object>();
 
@@ -88,6 +88,4 @@
                 long cummulatedWaitingTime = 0;
                 int numberOfAdditionalClicks = 0;
-                
-                System.out.println("\n\n#################################");
                 
                 Event exampleEvent = null;
@@ -101,5 +99,4 @@
                         for (List<Event> subsequence : clicksOnIdenticalButton) {
                             exampleEvent = subsequence.get(0);
-                            System.out.println(exampleEvent.getTimestamp());
                             
                             Event endEvent = subsequence.get(subsequence.size() - 1);
@@ -123,6 +120,6 @@
                 parameters.put("task", entry.getKey());
                 
-                results.addDefect
-                    (severity, UsabilityDefectDescription.MISSING_FEEDBACK, parameters);
+                results.addSmell(entry.getKey(), intensity,
+                                 UsabilitySmellDescription.MISSING_FEEDBACK, parameters);
             }
         }
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/RequiredInefficientActionsRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/RequiredInefficientActionsRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/RequiredInefficientActionsRule.java	(revision 1918)
@@ -67,6 +67,6 @@
             int ratio = (int) (1000 * stats.getMean());
 
-            UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-                (ratio, 500, 300, 200, 100, entry.getKey(), taskModel);
+            UsabilitySmellIntensity severity =
+                UsabilitySmellIntensity.getIntensity(ratio, entry.getKey(), taskModel);
 
             if (severity != null) {
@@ -75,6 +75,6 @@
                 parameters.put("ratio", (ratio / 10));
 
-                results.addDefect
-                    (severity, UsabilityDefectDescription.INEFFICIENT_ACTIONS, parameters);
+                results.addSmell(entry.getKey(), severity,
+                                 UsabilitySmellDescription.INEFFICIENT_ACTIONS, parameters);
             }
         }
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/RuleUtils.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/RuleUtils.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/RuleUtils.java	(revision 1918)
@@ -0,0 +1,265 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.Collection;
+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 de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementGroup;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @author Patrick Harms
+ */
+class RuleUtils {
+
+    /**
+     *
+     */
+    static Map<IGUIElement, List<IGUIElement>> getGroups(Collection<IGUIElement> guiElements,
+                                                         int                     maxDistToCommonParent)
+    {
+        Map<IGUIElement, List<IGUIElement>> groups = new HashMap<>();
+        List<IGUIElement> guiElementsToGroup = new LinkedList<>(guiElements);
+        IGUIElement parentToGroup;
+        
+        do {
+            List<LinkedList<IGUIElement>> sortedPaths = new LinkedList<>();
+            List<IGUIElement> commonParents = new LinkedList<>();
+            
+            createSortedPaths
+                (guiElementsToGroup, sortedPaths, commonParents, maxDistToCommonParent);
+
+            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION
+            // Iterator<LinkedList<IGUIElement>> sortedPathsIt1 = sortedPaths.iterator();
+            // Iterator<IGUIElement> commonParentIt1 = commonParents.iterator();
+            //  
+            // while (commonParentIt1.hasNext()) {
+            //     IGUIElement currentParent = commonParentIt1.next();
+            //     IGUIElement sortedPath = sortedPathsIt1.next().getLast();
+            //     
+            //     System.out.println(toPathString(sortedPath));
+            //     System.out.println(toPathString(currentParent) + "###########");
+            // }
+            //  
+            // System.out.println();
+            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION
+            
+            parentToGroup = getParentToGroup(commonParents);
+        
+            if (parentToGroup != null) {
+                Iterator<LinkedList<IGUIElement>> sortedPathsIt = sortedPaths.iterator();
+                ListIterator<IGUIElement> commonParentIt = commonParents.listIterator();
+        
+                while (commonParentIt.hasNext()) {
+                    if (!parentToGroup.equals(commonParentIt.next())) {
+                        sortedPathsIt.next();
+                    }
+                    else {
+                        break;
+                    }
+                }
+                
+                List<IGUIElement> groupedGUIElements = new LinkedList<>();
+                
+                // go one backward to ensure, that next will return the first occurrence
+                // of the parent to create the group for.
+                commonParentIt.previous();
+                
+                do {
+                    IGUIElement guiElementToGroup = sortedPathsIt.next().getLast();
+                    groupedGUIElements.add(guiElementToGroup);
+                    guiElementsToGroup.remove(guiElementToGroup);
+                    
+                }
+                while (parentToGroup.equals(commonParentIt.next()));
+                
+                groups.put(parentToGroup, groupedGUIElements);
+                
+                // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION
+                // System.out.println("group for");
+                // System.out.println(toPathString(parentToGroup));
+                //  
+                // for (IGUIElement element : groupedGUIElements) {
+                //     System.out.println(toPathString(element));
+                // }
+                //  
+                // System.out.println();
+                // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION
+            }
+        }
+        while (parentToGroup != null);
+        
+        return groups;
+    }
+
+    /**
+     *
+     */
+    static String toPathString(IGUIElement element) {
+        IGUIElement parent = element;
+        String result = "";
+        
+        while (parent != null) {
+            if (!(parent instanceof GUIElementGroup)) {
+                result = parent.toString() + "/" + result;
+            }
+            else {
+                result = parent.toString().hashCode() + "/" + result;
+            }
+            
+            parent = parent.getParent();
+        }
+        
+        return result.toString();
+    }
+
+    /**
+     *
+     */
+    private static void createSortedPaths(List<IGUIElement>             guiElements,
+                                          List<LinkedList<IGUIElement>> sortedPaths,
+                                          List<IGUIElement>             commonParents,
+                                          int                           maxDistToCommonParent)
+    {
+        for (IGUIElement guiElement : guiElements) {
+            // create the path
+            LinkedList<IGUIElement> path = new LinkedList<>();
+            IGUIElement parent = guiElement;
+            while (parent != null) {
+                if (!(parent instanceof GUIElementGroup)) {
+                    path.addFirst(parent);
+                }
+                
+                parent = parent.getParent();
+            }
+            
+            // sort it into the list of paths
+            int maxEquality = 0;
+            int maxEqualityPos = 0;
+            int pos = 0;
+            
+            for (List<IGUIElement> candidate : sortedPaths) {
+                int equality = 0;
+                while ((equality < candidate.size()) && (equality < path.size()) &&
+                       (candidate.get(equality).equals(path.get(equality))))
+                {
+                    equality++;
+                }
+                
+                if (equality > maxEquality) {
+                    maxEquality = equality;
+                    maxEqualityPos = pos;
+                }
+                
+                pos++;
+            }
+            
+            sortedPaths.add(maxEqualityPos, path);
+            
+            if ((maxEquality > 0) && ((path.size() - maxEquality) < maxDistToCommonParent)) {
+                commonParents.add(maxEqualityPos, path.get(maxEquality - 1));
+            }
+            else {
+                commonParents.add(maxEqualityPos, null);
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    private static IGUIElement getParentToGroup(List<IGUIElement> commonParents) {
+        Map<IGUIElement, Integer> occurrenceCounts = new HashMap<>();
+        Map<IGUIElement, Integer> depths = new HashMap<>();
+        
+        // get the required information about path lengths and occurrence counts
+        for (IGUIElement commonParent : commonParents) {
+            Integer occurrenceCount = occurrenceCounts.get(commonParent);
+            
+            if (occurrenceCount != null) {
+                occurrenceCounts.put(commonParent, occurrenceCount + 1);
+            }
+            else {
+                occurrenceCounts.put(commonParent, 1);
+            }
+            
+            if (!depths.containsKey(commonParent)) {
+                int depth = 0;
+                IGUIElement parent = commonParent;
+                
+                while (parent != null) {
+                    depth++;
+                    parent = parent.getParent();
+                }
+                
+                depths.put(commonParent, depth);
+            }
+        }
+        
+        IGUIElement elementToGroup = null;
+        
+        // get the GUI element being the parent most often 
+        for (IGUIElement commonParent : commonParents) {
+            if (elementToGroup == null) {
+                elementToGroup = commonParent;
+            }
+            else if ((commonParent != null) &&
+                     (!elementToGroup.equals(commonParent)))
+            {
+                int occurrenceCountCandidate = occurrenceCounts.get(commonParent);
+                int depthCandidate = depths.get(commonParent);
+            
+                int occurrenceCountElement = occurrenceCounts.get(elementToGroup);
+                int depthElement = depths.get(elementToGroup);
+                
+                if ((depthCandidate > depthElement) ||
+                    ((depthCandidate == depthElement) &&
+                     (occurrenceCountCandidate > occurrenceCountElement)))
+                {
+                    elementToGroup = commonParent;
+                }
+                else if ((occurrenceCountCandidate == occurrenceCountElement) &&
+                         (depthCandidate == depthElement))
+                {
+                    // in this situation, the order is irrelevant. The GUI elements for which
+                    // both paths were identified as parents are completely different.
+                    // Otherwise, they would have the same parent. But as they are that
+                    // different, they will not occur subsequently in the ordered list
+                    // of GUI elements to group. Hence, we just reuse the one the is currently
+                    // identified as the one to group next.
+                }
+            }
+        }
+        
+        return elementToGroup;
+    }
+
+    /**
+     * 
+     */
+    private RuleUtils() {
+        // prevent instantiation
+    }
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TargetDistanceRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TargetDistanceRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TargetDistanceRule.java	(revision 1918)
@@ -136,6 +136,6 @@
             // for HTML: 600 means not on the same page
             // for HTML: 501 means in average not on the same page
-            UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-                (ratio, 800, 600, 501, 501, task, taskModel);
+            UsabilitySmellIntensity severity =
+                UsabilitySmellIntensity.getIntensity(ratio, task, taskModel);
 
             if (severity != null) {
@@ -148,6 +148,6 @@
                 parameters.put("distance", ((double) ratio / 1000));
 
-                results.addDefect
-                    (severity, UsabilityDefectDescription.HIGH_TARGET_DISTANCE, parameters);
+                results.addSmell
+                    (task, severity, UsabilitySmellDescription.HIGH_TARGET_DISTANCE, parameters);
             }
         }
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TaskCooccurrenceRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TaskCooccurrenceRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TaskCooccurrenceRule.java	(revision 1918)
@@ -62,4 +62,6 @@
                 int index2 = 1;
                 List<ITask> children = sequence.getChildren();
+                ITaskInfo infoSequence = taskModel.getTaskInfo(sequence);
+                int countSequence = infoSequence.getMeasureValue(TaskMetric.COUNT);
                 
                 while (index2 < children.size()) {
@@ -69,11 +71,9 @@
                     ITaskInfo info2 = taskModel.getTaskInfo(task2);
                     
-                    int ratioTask1 = 1000 * info1.getMeasureValue(TaskMetric.COUNT, sequence) /
-                        info1.getMeasureValue(TaskMetric.COUNT);
-                    int ratioTask2 = 1000 * info2.getMeasureValue(TaskMetric.COUNT, sequence) /
-                            info2.getMeasureValue(TaskMetric.COUNT);
+                    int ratioTask1 = 1000 * countSequence / info1.getMeasureValue(TaskMetric.COUNT);
+                    int ratioTask2 = 1000 * countSequence / info2.getMeasureValue(TaskMetric.COUNT);
                     
-                    createSucceededDefectIfRequired(ratioTask1, task1, task2, results, taskModel);
-                    createPrecededDefectIfRequired(ratioTask2, task1, task2, results, taskModel);
+                    createSucceededSmellIfRequired(ratioTask1, task1, task2, results, taskModel);
+                    createPrecededSmellIfRequired(ratioTask2, task1, task2, results, taskModel);
                     
                     index1 = index2;
@@ -87,13 +87,13 @@
      *
      */
-    private void createSucceededDefectIfRequired(int                       ratio,
-                                                 ITask                     task1,
-                                                 ITask                     task2,
-                                                 UsabilityEvaluationResult results,
-                                                 ITaskModel                taskModel)
+    private void createSucceededSmellIfRequired(int                       ratio,
+                                                ITask                     task1,
+                                                ITask                     task2,
+                                                UsabilityEvaluationResult results,
+                                                ITaskModel                taskModel)
     {
         //TODO document magic numbers
-        UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-            (ratio, 900, 700, 500, 300, task1, taskModel);
+        UsabilitySmellIntensity severity =
+            UsabilitySmellIntensity.getIntensity(ratio, task1, taskModel);
 
         if (!isScroll(task1) && !isScroll(task2) && (severity != null)) {
@@ -103,5 +103,6 @@
             parameters.put("ratio", (ratio / 10));
 
-            results.addDefect(severity, UsabilityDefectDescription.COOCCURENCE_SUCCEED, parameters);
+            results.addSmell
+                (task1, severity, UsabilitySmellDescription.COOCCURENCE_SUCCEED, parameters);
         }
     }
@@ -110,13 +111,13 @@
      *
      */
-    private void createPrecededDefectIfRequired(int                       ratio,
-                                                ITask                     task1,
-                                                ITask                     task2,
-                                                UsabilityEvaluationResult results,
-                                                ITaskModel                taskModel)
+    private void createPrecededSmellIfRequired(int                       ratio,
+                                               ITask                     task1,
+                                               ITask                     task2,
+                                               UsabilityEvaluationResult results,
+                                               ITaskModel                taskModel)
     {
         //TODO document magic numbers
-        UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-            (ratio, 900, 700, 500, 300, task2, taskModel);
+        UsabilitySmellIntensity severity =
+            UsabilitySmellIntensity.getIntensity(ratio, task2, taskModel);
 
         if (!isScroll(task1) && !isScroll(task2) && (severity != null)) {
@@ -126,5 +127,6 @@
             parameters.put("ratio", (ratio / 10));
 
-            results.addDefect(severity, UsabilityDefectDescription.COOCCURENCE_PRECED, parameters);
+            results.addSmell
+                (task1, severity, UsabilitySmellDescription.COOCCURENCE_PRECED, parameters);
         }
     }
@@ -140,5 +142,6 @@
     private boolean isScroll(ITask task) {
         if (task instanceof IEventTask) {
-            return ((IEventTaskInstance) ((IEventTask) task).getInstances().iterator().next()).getEvent().getType() instanceof Scroll;
+            return ((IEventTaskInstance) ((IEventTask) task).getInstances().iterator().next())
+                .getEvent().getType() instanceof Scroll;
         }
         else if (task instanceof IIteration) {
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TaskTreeTestRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TaskTreeTestRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TaskTreeTestRule.java	(revision 1918)
@@ -15,7 +15,19 @@
 package de.ugoe.cs.autoquest.usability;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 import de.ugoe.cs.autoquest.eventcore.Event;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceVisitor;
@@ -41,6 +53,119 @@
 
         checkTimestamps(taskModel);
+        checkTaskInfos(taskModel);
+        checkEventTaskInstances(taskModel);
+        checkTraversal(taskModel);
 
         return results;
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param taskModel
+     */
+    private void checkEventTaskInstances(ITaskModel taskModel) {
+        Set<IEventTaskInstance> instancesInModel = new HashSet<>();
+        
+        for (ITask task : taskModel.getTasks()) {
+            if (task instanceof IEventTask) {
+                for (ITaskInstance instance : task.getInstances()) {
+                    instancesInModel.add((IEventTaskInstance) instance);
+                }
+            }
+        }
+        
+        final Set<IEventTaskInstance> instancesInSessions = new HashSet<>();
+        
+        for (IUserSession session : taskModel.getUserSessions()) {
+            for (ITaskInstance rootInstance : session) {
+                rootInstance.accept(new DefaultTaskInstanceTraversingVisitor() {
+                    @Override
+                    public void visit(IEventTaskInstance eventTaskInstance) {
+                        instancesInSessions.add(eventTaskInstance);
+                    }
+                });
+            }
+        }
+        
+        if (instancesInModel.size() != instancesInSessions.size()) {
+            throw new RuntimeException("instances not equal");
+        }
+        else {
+            for (IEventTaskInstance instance : instancesInModel) {
+                if (!instancesInSessions.contains(instance)) {
+                    throw new RuntimeException("instance of model not contained in sessions");
+                }
+            }
+            
+            for (IEventTaskInstance instance : instancesInSessions) {
+                if (!instancesInModel.contains(instance)) {
+                    throw new RuntimeException("instance of sessions not contained in model");
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param taskModel
+     */
+    private void checkTraversal(ITaskModel taskModel) {
+        for (ITask task : taskModel.getTasks()) {
+            final List<ITask> traversal1 = new ArrayList<>();
+            
+            task.accept(new DefaultTaskTraversingVisitor() {
+                @Override
+                public void visit(IEventTask eventTask) {
+                    traversal1.add(eventTask);
+                    super.visit(eventTask);
+                }
+
+                @Override
+                public void visit(IStructuringTemporalRelationship relationship) {
+                    traversal1.add(relationship);
+                    super.visit(relationship);
+                }
+                
+            });
+            
+            final List<ITask> traversal2 = new ArrayList<>();
+            
+            task.accept(new DefaultTaskTraversingVisitor() {
+                @Override
+                public void visit(IEventTask eventTask) {
+                    traversal2.add(eventTask);
+                    super.visit(eventTask);
+                }
+
+                @Override
+                public void visit(ISelection selection) {
+                    traversal2.add(selection);
+                    super.visit(selection);
+                }
+
+                @Override
+                public void visit(ISequence sequence) {
+                    traversal2.add(sequence);
+                    super.visit(sequence);
+                }
+            });
+            
+            if (traversal1.size() != traversal2.size()) {
+                throw new RuntimeException("traversals not equal");
+            }
+            else {
+                for (int i = 0; i < traversal1.size(); i++) {
+                    if (!traversal1.get(i).equals(traversal2.get(i))) {
+                        throw new RuntimeException("traversals not equal at position " + i);
+                    }
+                }
+            }
+        }
     }
 
@@ -78,3 +203,19 @@
         
     }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param taskModel
+     */
+    private void checkTaskInfos(ITaskModel taskModel) {
+        for (ITask task : taskModel.getTasks()) {
+            ITaskInfo taskInfo = taskModel.getTaskInfo(task);
+            if (taskInfo == null) {
+                System.out.println("task " + task + " has no associated task infos");
+            }
+        }
+    }
 }
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 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java	(revision 1918)
@@ -84,7 +84,5 @@
         int ratio = 1000 * allTextFieldInputs / statistics.getNoOfAllEvents();
 
-        // TODO comment magic numbers
-        UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-            (ratio, 900, 700, 500, 300);
+        UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity(ratio);
         
         if (severity != null) {
@@ -92,6 +90,6 @@
             parameters.put("textInputRatio", (ratio / 10));
 
-            results.addDefect
-                (severity, UsabilityDefectDescription.TEXT_FIELD_INPUT_RATIO, parameters);
+            results.addSmell
+                (severity, UsabilitySmellDescription.TEXT_FIELD_INPUT_RATIO, parameters);
         }
     }
@@ -117,8 +115,8 @@
                 1000 * noOfUsagesOfTextField1WithSameTextInTextField2 / noOfUsagesOfTextField2;
 
-            createTextFieldEntryRepetitionDefect(ratioTextField1, entry.textField1,
+            createTextFieldEntryRepetitionSmell(ratioTextField1, entry.textField1,
                                                  entry.textField2, results);
             
-            createTextFieldEntryRepetitionDefect(ratioTextField2, entry.textField2,
+            createTextFieldEntryRepetitionSmell(ratioTextField2, entry.textField2,
                                                  entry.textField1, results);
             
@@ -129,12 +127,11 @@
      *
      */
-    private void createTextFieldEntryRepetitionDefect(int                       ratioOfEqualEntries,
+    private void createTextFieldEntryRepetitionSmell(int                       ratioOfEqualEntries,
                                                       ITextField                textField1,
                                                       ITextField                textField2,
                                                       UsabilityEvaluationResult results)
     {
-        // TODO comment magic numbers
-        UsabilityDefectSeverity severity = UsabilityDefectSeverity.getSeverity
-            (ratioOfEqualEntries, 900, 500, 200, 100);
+        UsabilitySmellIntensity severity =
+            UsabilitySmellIntensity.getIntensity(ratioOfEqualEntries);
         
         if (severity != null) {
@@ -144,6 +141,6 @@
             parameters.put("textField2", textField2);
 
-            results.addDefect
-                (severity, UsabilityDefectDescription.TEXT_FIELD_INPUT_REPETITIONS, parameters);
+            results.addSmell
+                (severity, UsabilitySmellDescription.TEXT_FIELD_INPUT_REPETITIONS, parameters);
         }
     }
@@ -172,11 +169,5 @@
             int ratio = 1000 * noLetterOrDigitCount / allCharactersCount;
 
-            UsabilityDefectSeverity severity =
-                UsabilityDefectSeverity.getSeverity(ratio,
-                                                    100, // every 10th sign
-                                                    50, // every 20th sign
-                                                    20, // every 50th sign
-                                                    10 // every 100th sign
-                                                    );
+            UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity(ratio);
 
             if (severity != null) {
@@ -185,6 +176,6 @@
                 parameters.put("noLetterOrDigitRatio", (ratio / 10));
 
-                results.addDefect
-                    (severity, UsabilityDefectDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
+                results.addSmell
+                    (severity, UsabilitySmellDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
                      parameters);
             }
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UnusedGUIElementsRule.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UnusedGUIElementsRule.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UnusedGUIElementsRule.java	(revision 1918)
@@ -78,6 +78,5 @@
         int ratio = 1000 * unusedGUIElements.size() / allGUIElements.size();
         
-        UsabilityDefectSeverity severity =
-            UsabilityDefectSeverity.getSeverity(ratio, 200, 100, 50, 25);
+        UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity(ratio);
 
         if (severity != null) {
@@ -89,6 +88,6 @@
             parameters.put("unusedGuiElements", unusedGUIElements);
             
-            results.addDefect
-                (severity, UsabilityDefectDescription.UNUSED_GUI_ELEMENTS, parameters);
+            results.addSmell
+                (severity, UsabilitySmellDescription.UNUSED_GUI_ELEMENTS, parameters);
         }
      }
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefect.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefect.java	(revision 1493)
+++ 	(revision )
@@ -1,134 +1,0 @@
-//   Copyright 2012 Georg-August-Universität Göttingen, Germany
-//
-//   Licensed under the Apache License, Version 2.0 (the "License");
-//   you may not use this file except in compliance with the License.
-//   You may obtain a copy of the License at
-//
-//       http://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-package de.ugoe.cs.autoquest.usability;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * TODO comment
- * 
- * @version $Revision: $ $Date: 16.07.2012$
- * @author 2012, last modified by $Author: pharms$
- */
-public class UsabilityDefect {
-
-    /** */
-    private UsabilityDefectSeverity severity;
-
-    /** */
-    private UsabilityDefectDescription description;
-
-    /** */
-    private Map<String, Object> descriptionParameters;
-
-    /**
-     *
-     */
-    UsabilityDefect(UsabilityDefectSeverity severity, UsabilityDefectDescription description) {
-        this(severity, description, null);
-    }
-
-    /**
-     *
-     */
-    UsabilityDefect(UsabilityDefectSeverity    severity,
-                    UsabilityDefectDescription description,
-                    Map<String, Object>        parameters)
-    {
-        this.severity = severity;
-        this.description = description;
-        this.descriptionParameters = parameters;
-    }
-
-    /**
-     * 
-     */
-    public UsabilityDefectSeverity getSeverity() {
-        return severity;
-    }
-
-    /**
-     *
-     */
-    public void setSeverity(UsabilityDefectSeverity severity) {
-        this.severity = severity;
-    }
-
-    /**
-     *
-     */
-    public void setDescription(UsabilityDefectDescription description) {
-        this.description = description;
-    }
-
-    /**
-     * 
-     */
-    public String getParameterizedDescription() {
-        return description.toString(descriptionParameters);
-    }
-
-    /**
-     * 
-     */
-    public List<Object> getDescriptionFragments() {
-        return description.toFragmentList(descriptionParameters);
-    }
-
-    /*
-     */
-    public String getBriefDescription() {
-        return description.getBriefDescription();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof UsabilityDefect) {
-            return
-                (severity == ((UsabilityDefect) obj).severity) &&
-                (description == ((UsabilityDefect) obj).description);
-        }
-        else {
-            return false;
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see java.lang.Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return severity.hashCode() + description.hashCode();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see java.lang.Object#toString()
-     */
-    @Override
-    public String toString() {
-        return "UsabilityDefect(" + severity.name() + ", " + description.name() + ")";
-    }
-
-}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescription.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectDescription.java	(revision 1493)
+++ 	(revision )
@@ -1,257 +1,0 @@
-//   Copyright 2012 Georg-August-Universität Göttingen, Germany
-//
-//   Licensed under the Apache License, Version 2.0 (the "License");
-//   you may not use this file except in compliance with the License.
-//   You may obtain a copy of the License at
-//
-//       http://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-package de.ugoe.cs.autoquest.usability;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.Unmarshaller;
-
-/**
- * TODO comment
- * 
- * @version $Revision: $ $Date: 18.07.2012$
- * @author 2012, last modified by $Author: pharms$
- */
-public enum UsabilityDefectDescription {
-    
-    INEFFICIENT_ACTIONS,
-    TEXT_FIELD_INPUT_RATIO,
-    TEXT_FIELD_INPUT_REPETITIONS,
-    TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
-    COOCCURENCE_SUCCEED,
-    COOCCURENCE_PRECED,
-    HIGH_EVENT_COVERAGE,
-    HIGH_TARGET_DISTANCE,
-    MISSING_FEEDBACK,
-    UNUSED_GUI_ELEMENTS;
-
-    /** */
-    private static final String DEFAULT_MESSAGES_FILE = "defectDescriptions_en.xml";
-
-    /** */
-    private static DefectDescriptions sDefectDescriptions;
-
-    /** */
-    private DefectDescription defectDescription;
-
-    /**
-     *
-     */
-    private UsabilityDefectDescription() {
-        init();
-    }
-
-    /**
-     *
-     */
-    @SuppressWarnings("unchecked")
-    private void init() {
-        synchronized (this.getClass()) {
-            if (sDefectDescriptions == null) {
-                InputStream inputStream =
-                    ClassLoader.getSystemResourceAsStream(DEFAULT_MESSAGES_FILE);
-
-                try {
-                    String packageName = DefectDescriptions.class.getPackage().getName();
-                    JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
-                    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
-
-                    sDefectDescriptions =
-                        ((JAXBElement<DefectDescriptions>) unmarshaller.unmarshal(inputStream))
-                            .getValue();
-                }
-                catch (Exception e) {
-                    throw new RuntimeException
-                        ("error while initializing usability defect descriptions", e);
-                }
-                finally {
-                    if (inputStream != null) {
-                        try {
-                            inputStream.close();
-                        }
-                        catch (IOException e) {
-                            // ignore
-                        }
-                    }
-                }
-            }
-        }
-
-        for (DefectDescription description : sDefectDescriptions.getDefectDescription()) {
-            if (this.name().equals(description.getDefectId())) {
-                defectDescription = description;
-                break;
-            }
-        }
-
-        if (defectDescription == null) {
-            throw new RuntimeException
-                ("error while initializing usability defect descriptions. No " +
-                 "description text available for description " + this.name());
-        }
-    }
-
-    /**
-     * 
-     */
-    public String[] getDescriptionParameters() {
-        List<String> parameters = new ArrayList<String>();
-
-        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
-            if (fragment instanceof ParameterFragment) {
-                parameters.add(((ParameterFragment) fragment).getParameterName());
-            }
-        }
-
-        return parameters.toArray(new String[parameters.size()]);
-    }
-
-    /**
-     * 
-     */
-    public String toString(Map<String, Object> parameters) throws IllegalArgumentException {
-        StringBuffer result = new StringBuffer();
-
-        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
-            if (result.length() > 0) {
-                result.append(" ");
-            }
-
-            if (fragment instanceof ParameterFragment) {
-                Object value = null;
-                if (parameters != null) {
-                    value = parameters.get(((ParameterFragment) fragment).getParameterName());
-                }
-
-                if (value != null) {
-                    if (value instanceof Collection<?>) {
-                        int counter = 1;
-                        for (Object elem : ((Collection<?>) value)) {
-                            result.append('\n');
-                            result.append(counter++);
-                            result.append(".: ");
-                            result.append(elem);
-                        }
-                    }
-                    else {
-                        result.append(value.toString());
-                    }
-                }
-                else {
-                    throw new IllegalArgumentException
-                        ("required parameter \"" +
-                         ((ParameterFragment) fragment).getParameterName() +
-                         "\" for usability defect description " + this.name() + " not provided");
-                }
-            }
-            else {
-                result.append(getFragmentString(fragment));
-            }
-        }
-
-        return result.toString();
-    }
-
-    /**
-     * 
-     */
-    public List<Object> toFragmentList(Map<String, Object> parameters)
-        throws IllegalArgumentException
-    {
-        List<Object> result = new ArrayList<Object>();
-
-        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
-            if (fragment instanceof ParameterFragment) {
-                Object value = null;
-                if (parameters != null) {
-                    value = parameters.get(((ParameterFragment) fragment).getParameterName());
-                }
-
-                if (value != null) {
-                    result.add(value);
-                }
-                else {
-                    throw new IllegalArgumentException
-                        ("required parameter \"" +
-                         ((ParameterFragment) fragment).getParameterName() +
-                         "\" for usability defect description " + this.name() + " not provided");
-                }
-            }
-            else {
-                result.add(getFragmentString(fragment));
-            }
-        }
-
-        return result;
-    }
-    
-    /**
-     * 
-     */
-    public String getBriefDescription() {
-        return defectDescription.briefText;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see java.lang.Enum#toString()
-     */
-    @Override
-    public String toString() {
-        StringBuffer result = new StringBuffer();
-
-        int paramCount = 1;
-        for (Object fragment : defectDescription.getTextFragmentOrParameterFragment()) {
-            if (result.length() > 0) {
-                result.append(" ");
-            }
-
-            if (fragment instanceof ParameterFragment) {
-                result.append("<parameter");
-                result.append(paramCount++);
-                result.append(">");
-            }
-            else {
-                result.append(getFragmentString(fragment));
-            }
-        }
-
-        return result.toString();
-    }
-
-    /**
-     *
-     */
-    private String getFragmentString(Object fragment) {
-        String fragmentStr = fragment.toString().trim();
-
-        fragmentStr = fragmentStr.replaceAll("\n", " ");
-
-        while (fragmentStr.indexOf("  ") > -1) {
-            fragmentStr = fragmentStr.replaceAll("  ", " ");
-        }
-
-        return fragmentStr;
-    }
-
-}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectSeverity.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityDefectSeverity.java	(revision 1493)
+++ 	(revision )
@@ -1,127 +1,0 @@
-//   Copyright 2012 Georg-August-Universität Göttingen, Germany
-//
-//   Licensed under the Apache License, Version 2.0 (the "License");
-//   you may not use this file except in compliance with the License.
-//   You may obtain a copy of the License at
-//
-//       http://www.apache.org/licenses/LICENSE-2.0
-//
-//   Unless required by applicable law or agreed to in writing, software
-//   distributed under the License is distributed on an "AS IS" BASIS,
-//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//   See the License for the specific language governing permissions and
-//   limitations under the License.
-
-package de.ugoe.cs.autoquest.usability;
-
-import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
-import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo;
-import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
-import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric;
-
-/**
- * TODO comment
- * 
- * @version $Revision: $ $Date: 16.07.2012$
- * @author 2012, last modified by $Author: pharms$
- */
-public enum UsabilityDefectSeverity {
-    
-    INFO, LOW, MEDIUM, HIGH;
-    
-    /** */
-    static int defaultCoverageQuantile = 950;
-
-    /**
-     * 
-     */
-    static UsabilityDefectSeverity getSeverity(int ratio,
-                                               int highRatioLevel,
-                                               int mediumRatioLevel,
-                                               int lowRatioLevel,
-                                               int infoRatioLevel)
-    {
-        return getSeverity(ratio, highRatioLevel, mediumRatioLevel, lowRatioLevel, infoRatioLevel,
-                           defaultCoverageQuantile);
-    }
-
-    /**
-     * 
-     */
-    static UsabilityDefectSeverity getSeverity(int        ratio,
-                                               int        highRatioLevel,
-                                               int        mediumRatioLevel,
-                                               int        lowRatioLevel,
-                                               int        infoRatioLevel,
-                                               ITask      taskWithDefect,
-                                               ITaskModel wholeTaskModel)
-    {
-        ITaskInfo taskInfo = wholeTaskModel.getTaskInfo(taskWithDefect);
-        int eventCoverageQuantile = taskInfo.getMeasureValue(TaskMetric.EVENT_COVERAGE_QUANTILE);
-        return getSeverity(ratio, highRatioLevel, mediumRatioLevel, lowRatioLevel, infoRatioLevel,
-                           eventCoverageQuantile);
-    }
-
-    /**
-     * 
-     */
-    static UsabilityDefectSeverity getSeverity(int ratio,
-                                               int highRatioLevel,
-                                               int mediumRatioLevel,
-                                               int lowRatioLevel,
-                                               int infoRatioLevel,
-                                               int coverageQuantile)
-    {
-        int effectiveRatio = ratio;
-        
-        // event coverage ratio is in per mille. The more executed events a task covers, the more
-        // important a related usability defect.
-        /*if (eventCoverageRatio < 1) {
-            // one per mille, so one of thousand events is covered
-            effectiveRatio *= 0.2;
-        }
-        else if (eventCoverageRatio < 5) {
-            // 5 per mille, so one of 250 events is covered
-            effectiveRatio *= 0.4;
-        }
-        else if (eventCoverageRatio < 10) {
-            // 1 percent, so one of 100 events is covered
-            effectiveRatio *= 0.5;
-        }
-        else if (eventCoverageRatio < 20) {
-            // 2 percent, so one of 50 events is covered
-            effectiveRatio *= 0.6;
-        }
-        else if (eventCoverageRatio < 30) {
-            // 3 percent, so one of 33 events is covered
-            effectiveRatio *= 0.7;
-        }
-        else if (eventCoverageRatio < 40) {
-            // 4 percent, so one of 28 events is covered
-            effectiveRatio *= 0.8;
-        }
-        else if (eventCoverageRatio < 50) {
-            // 5 percent, so one of 25 events is covered
-            effectiveRatio *= 0.9;
-        }*/
-        //else {
-            // more than 5 percent, so 1 of 20 events, do not change ratio
-        //}
-        if (coverageQuantile >= defaultCoverageQuantile) {
-            if (effectiveRatio >= highRatioLevel) {
-                return UsabilityDefectSeverity.HIGH;
-            }
-            else if (effectiveRatio >= mediumRatioLevel) {
-                return UsabilityDefectSeverity.MEDIUM;
-            }
-            else if (effectiveRatio >= lowRatioLevel) {
-                return UsabilityDefectSeverity.LOW;
-            }
-            else if (effectiveRatio >= infoRatioLevel) {
-                return UsabilityDefectSeverity.INFO;
-            }
-        }
-        
-        return null;
-    }
-}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java	(revision 1918)
@@ -48,12 +48,18 @@
      */
     private void init() {
-        //rules.add(new TextInputStatisticsRule());
-        rules.add(new MissingFeedbackRule());
-        rules.add(new EventCoverageRatioRule());
-        rules.add(new RequiredInefficientActionsRule());
-        //rules.add(new TaskCooccurrenceRule());
-        rules.add(new TargetDistanceRule());
-        //rules.add(new UnusedGUIElementsRule());
-        //rules.add(new TaskTreeTestRule());
+        rules.add(new TaskTreeTestRule());
+//        rules.add(new TextInputStatisticsRule());
+//        rules.add(new MissingFeedbackRule());
+//        rules.add(new EventCoverageRatioRule());
+//        rules.add(new TargetDistanceRule());
+//        rules.add(new RequiredInefficientActionsRule());
+//        rules.add(new DataEntryMethodChangeRule());
+        rules.add(new DefaultValueRule());
+//        rules.add(new CheckBoxMultipleSelectionRule());
+//        rules.add(new CommonTaskRateRule());
+//        rules.add(new MisleadingClickCueRule());
+//        rules.add(new DefaultCursorPositioningRule());
+//        rules.add(new UnusedGUIElementsRule());
+//        rules.add(new TaskCooccurrenceRule());
     }
 
@@ -70,40 +76,35 @@
             UsabilityEvaluationResult result = rule.evaluate(taskModel);
             interimResults.add(result);
-            Console.traceln(Level.INFO, "the rule found " + result.getAllDefects().size() +
-                            " usability defects, of which " + result.getSevereDefects().size() +
-                            " are severe.");
+            Console.traceln(Level.INFO, "the rule found " + result.getAllSmells().size() +
+                            " usability smells.");
             
-            if ((rule instanceof EventCoverageRatioRule) ||
-                (rule instanceof RequiredInefficientActionsRule) ||
-                (rule instanceof TargetDistanceRule))
-            {
-                ITask[] referredTasks = new ITask[result.getAllDefects().size()];
+            List<ITask> referredTasks = new ArrayList<ITask>();
 
-                for (int i = 0; i < result.getAllDefects().size(); i++) {
-                    referredTasks[i] =
-                        (ITask) result.getAllDefects().get(i).getDescriptionFragments().get(1);
+            for (UsabilitySmell smell : result.getAllSmells()) {
+                if (smell.getSmellingTask() != null) {
+                    referredTasks.add(smell.getSmellingTask());
                 }
+            }
                 
-                int counter = 0;
-                for (int i = 0; i < referredTasks.length; i++) {
-                    for (int j = 0; j < referredTasks.length; j++) {
-                        if (isChildOf(referredTasks[i], referredTasks[j])) {
-                            counter++;
-                            break;
-                        }
+            int counter = 0;
+            for (int i = 0; i < referredTasks.size(); i++) {
+                for (int j = 0; j < referredTasks.size(); j++) {
+                    if (isChildOf(referredTasks.get(i), referredTasks.get(j))) {
+                        counter++;
+                        break;
                     }
                 }
+            }
                 
-                if (counter > 0) {
-                    Console.traceln(Level.INFO, counter + " of the findings are duplicates in " +
-                                    "that they refer to tasks whose parent tasks are also " +
-                                    "referred by the findings");
-                }
+            if (counter > 0) {
+                Console.traceln(Level.INFO, counter + " of the findings are duplicates in " +
+                                "that they refer to tasks whose parent tasks are also " +
+                                "referred by the findings");
             }
         }
 
         UsabilityEvaluationResult result = new UsabilityEvaluationResult(taskModel, interimResults);
-        Console.println("the evaluation result contains " + result.getAllDefects().size() +
-                        " defects, of which " + result.getSevereDefects().size() + " are severe.");
+        Console.println("the evaluation result contains " + result.getAllSmells().size() +
+                        " smells.");
 
         return result;
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java	(revision 1493)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java	(revision 1918)
@@ -19,4 +19,5 @@
 import java.util.Map;
 
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
 
@@ -33,5 +34,5 @@
     
     /** */
-    private List<UsabilityDefect> defects = new ArrayList<UsabilityDefect>();
+    private List<UsabilitySmell> smells = new ArrayList<UsabilitySmell>();
 
     /**
@@ -50,6 +51,6 @@
         this.taskModel = taskModel;
         for (UsabilityEvaluationResult result : results) {
-            for (UsabilityDefect defect : result.getAllDefects()) {
-                defects.add(defect);
+            for (UsabilitySmell smell : result.getAllSmells()) {
+                smells.add(smell);
             }
         }
@@ -59,9 +60,9 @@
      *
      */
-    public void addDefect(UsabilityDefectSeverity    severity,
-                          UsabilityDefectDescription description,
-                          Map<String, Object>        parameters)
+    public void addSmell(UsabilitySmellIntensity   intensity,
+                         UsabilitySmellDescription description,
+                         Map<String, Object>       parameters)
     {
-        defects.add(new UsabilityDefect(severity, description, parameters));
+        addSmell(null, intensity, description, parameters);
     }
 
@@ -69,6 +70,10 @@
      *
      */
-    public List<UsabilityDefect> getAllDefects() {
-        return defects;
+    public void addSmell(ITask                     smellingTask,
+                         UsabilitySmellIntensity   intensity,
+                         UsabilitySmellDescription description,
+                         Map<String, Object>       parameters)
+    {
+        smells.add(new UsabilitySmell(smellingTask, intensity, description, parameters));
     }
 
@@ -76,14 +81,6 @@
      *
      */
-    public List<UsabilityDefect> getSevereDefects() {
-        List<UsabilityDefect> severeDefects = new ArrayList<UsabilityDefect>();
-
-        for (UsabilityDefect defect : defects) {
-            if (defect.getSeverity() == UsabilityDefectSeverity.HIGH) {
-                severeDefects.add(defect);
-            }
-        }
-
-        return severeDefects;
+    public List<UsabilitySmell> getAllSmells() {
+        return smells;
     }
 
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmell.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmell.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmell.java	(revision 1918)
@@ -0,0 +1,172 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.util.List;
+import java.util.Map;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class UsabilitySmell {
+
+    /** */
+    private ITask smellingTask;
+    
+    /** */
+    private UsabilitySmellIntensity intensity;
+
+    /** */
+    private UsabilitySmellDescription description;
+
+    /** */
+    private Map<String, Object> descriptionParameters;
+
+    /**
+     *
+     */
+    UsabilitySmell(UsabilitySmellIntensity intensity, UsabilitySmellDescription description) {
+        this(intensity, description, null);
+    }
+    
+    /**
+     *
+     */
+    UsabilitySmell(ITask                     smellingTask,
+                   UsabilitySmellIntensity   intensity,
+                   UsabilitySmellDescription description)
+    {
+        this(intensity, description, null);
+    }
+
+    /**
+     *
+     */
+    UsabilitySmell(UsabilitySmellIntensity   intensity,
+                   UsabilitySmellDescription description,
+                   Map<String, Object>       parameters)
+    {
+        this(null, intensity, description, parameters);
+    }
+
+    /**
+     *
+     */
+    UsabilitySmell(ITask                     smellingTask,
+                   UsabilitySmellIntensity   intensity,
+                   UsabilitySmellDescription description,
+                   Map<String, Object>       parameters)
+    {
+        this.smellingTask = smellingTask;
+        this.intensity = intensity;
+        this.description = description;
+        this.descriptionParameters = parameters;
+    }
+
+    /**
+     * 
+     */
+    public ITask getSmellingTask() {
+        return smellingTask;
+    }
+
+    /**
+     * 
+     */
+    public UsabilitySmellIntensity getIntensity() {
+        return intensity;
+    }
+
+    /**
+     *
+     */
+    public void setIntensity(UsabilitySmellIntensity intensity) {
+        this.intensity = intensity;
+    }
+
+    /**
+     *
+     */
+    public void setDescription(UsabilitySmellDescription description) {
+        this.description = description;
+    }
+
+    /**
+     * 
+     */
+    public String getParameterizedDescription() {
+        return description.toString(descriptionParameters);
+    }
+
+    /**
+     * 
+     */
+    public List<Object> getDescriptionFragments() {
+        return description.toFragmentList(descriptionParameters);
+    }
+
+    /*
+     */
+    public String getBriefDescription() {
+        return description.getBriefDescription();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof UsabilitySmell) {
+            return description.equals(((UsabilitySmell) obj).description);
+        }
+        else {
+            return false;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return description.hashCode();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        if (smellingTask == null) {
+            return "UsabilitySmell(" + intensity + ", " + description.name() + ")";
+        }
+        else {
+            return "UsabilitySmell(" + smellingTask + ", " + intensity + ", " +
+                description.name() + ")";
+        }
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellDescription.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellDescription.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellDescription.java	(revision 1918)
@@ -0,0 +1,263 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Unmarshaller;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 18.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public enum UsabilitySmellDescription {
+    
+    INEFFICIENT_ACTIONS,
+    TEXT_FIELD_INPUT_RATIO,
+    TEXT_FIELD_INPUT_REPETITIONS,
+    TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
+    COOCCURENCE_SUCCEED,
+    COOCCURENCE_PRECED,
+    HIGH_EVENT_COVERAGE,
+    HIGH_TARGET_DISTANCE,
+    MISSING_FEEDBACK,
+    UNUSED_GUI_ELEMENTS,
+    DATA_ENTRY_METHOD_CHANGE,
+    GOOD_DEFAULTS,
+    CHECK_BOX_SINGLE_SELECTION,
+    COMMON_TASK_RATE,
+    MISLEADING_CLICK_CUE,
+    MOST_OFTEN_DONE_FIRST;
+
+    /** */
+    private static final String DEFAULT_MESSAGES_FILE = "smellDescriptions_en.xml";
+
+    /** */
+    private static SmellDescriptions sSmellDescriptions;
+
+    /** */
+    private SmellDescription smellDescription;
+
+    /**
+     *
+     */
+    private UsabilitySmellDescription() {
+        init();
+    }
+
+    /**
+     *
+     */
+    @SuppressWarnings("unchecked")
+    private void init() {
+        synchronized (this.getClass()) {
+            if (sSmellDescriptions == null) {
+                InputStream inputStream =
+                    ClassLoader.getSystemResourceAsStream(DEFAULT_MESSAGES_FILE);
+
+                try {
+                    String packageName = SmellDescriptions.class.getPackage().getName();
+                    JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
+                    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+
+                    sSmellDescriptions =
+                        ((JAXBElement<SmellDescriptions>) unmarshaller.unmarshal(inputStream))
+                            .getValue();
+                }
+                catch (Exception e) {
+                    throw new RuntimeException
+                        ("error while initializing usability smell descriptions", e);
+                }
+                finally {
+                    if (inputStream != null) {
+                        try {
+                            inputStream.close();
+                        }
+                        catch (IOException e) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+        }
+
+        for (SmellDescription description : sSmellDescriptions.getSmellDescription()) {
+            if (this.name().equals(description.getSmellId())) {
+                smellDescription = description;
+                break;
+            }
+        }
+
+        if (smellDescription == null) {
+            throw new RuntimeException
+                ("error while initializing usability smell descriptions. No " +
+                 "description text available for description " + this.name());
+        }
+    }
+
+    /**
+     * 
+     */
+    public String[] getDescriptionParameters() {
+        List<String> parameters = new ArrayList<String>();
+
+        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
+            if (fragment instanceof ParameterFragment) {
+                parameters.add(((ParameterFragment) fragment).getParameterName());
+            }
+        }
+
+        return parameters.toArray(new String[parameters.size()]);
+    }
+
+    /**
+     * 
+     */
+    public String toString(Map<String, Object> parameters) throws IllegalArgumentException {
+        StringBuffer result = new StringBuffer();
+
+        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
+            if (result.length() > 0) {
+                result.append(" ");
+            }
+
+            if (fragment instanceof ParameterFragment) {
+                Object value = null;
+                if (parameters != null) {
+                    value = parameters.get(((ParameterFragment) fragment).getParameterName());
+                }
+
+                if (value != null) {
+                    if (value instanceof Collection<?>) {
+                        int counter = 1;
+                        for (Object elem : ((Collection<?>) value)) {
+                            result.append('\n');
+                            result.append(counter++);
+                            result.append(".: ");
+                            result.append(elem);
+                        }
+                    }
+                    else {
+                        result.append(value.toString());
+                    }
+                }
+                else {
+                    throw new IllegalArgumentException
+                        ("required parameter \"" +
+                         ((ParameterFragment) fragment).getParameterName() +
+                         "\" for usability smell description " + this.name() + " not provided");
+                }
+            }
+            else {
+                result.append(getFragmentString(fragment));
+            }
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * 
+     */
+    public List<Object> toFragmentList(Map<String, Object> parameters)
+        throws IllegalArgumentException
+    {
+        List<Object> result = new ArrayList<Object>();
+
+        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
+            if (fragment instanceof ParameterFragment) {
+                Object value = null;
+                if (parameters != null) {
+                    value = parameters.get(((ParameterFragment) fragment).getParameterName());
+                }
+
+                if (value != null) {
+                    result.add(value);
+                }
+                else {
+                    throw new IllegalArgumentException
+                        ("required parameter \"" +
+                         ((ParameterFragment) fragment).getParameterName() +
+                         "\" for usability smell description " + this.name() + " not provided");
+                }
+            }
+            else {
+                result.add(getFragmentString(fragment));
+            }
+        }
+
+        return result;
+    }
+    
+    /**
+     * 
+     */
+    public String getBriefDescription() {
+        return smellDescription.briefText;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Enum#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuffer result = new StringBuffer();
+
+        int paramCount = 1;
+        for (Object fragment : smellDescription.getTextFragmentOrParameterFragment()) {
+            if (result.length() > 0) {
+                result.append(" ");
+            }
+
+            if (fragment instanceof ParameterFragment) {
+                result.append("<parameter");
+                result.append(paramCount++);
+                result.append(">");
+            }
+            else {
+                result.append(getFragmentString(fragment));
+            }
+        }
+
+        return result.toString();
+    }
+
+    /**
+     *
+     */
+    private String getFragmentString(Object fragment) {
+        String fragmentStr = fragment.toString().trim();
+
+        fragmentStr = fragmentStr.replaceAll("\n", " ");
+
+        while (fragmentStr.indexOf("  ") > -1) {
+            fragmentStr = fragmentStr.replaceAll("  ", " ");
+        }
+
+        return fragmentStr;
+    }
+
+}
Index: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellIntensity.java
===================================================================
--- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellIntensity.java	(revision 1918)
+++ trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellIntensity.java	(revision 1918)
@@ -0,0 +1,119 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.usability;
+
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
+import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric;
+
+/**
+ * TODO comment
+ * 
+ * @version $Revision: $ $Date: 16.07.2012$
+ * @author 2012, last modified by $Author: pharms$
+ */
+public class UsabilitySmellIntensity {
+    
+    /** */
+    private int ratio;
+    
+    /** */
+    private int eventCoverage;
+    
+    /** */
+    private int eventCoverageQuantile;
+
+    /**
+     * 
+     */
+    static UsabilitySmellIntensity getIntensity(int        ratio,
+                                                ITask      taskWithSmell,
+                                                ITaskModel wholeTaskModel)
+    {
+        ITaskInfo taskInfo = wholeTaskModel.getTaskInfo(taskWithSmell);
+        int eventCoverage = taskInfo.getMeasureValue(TaskMetric.EVENT_COVERAGE);
+        int eventCoverageQuantile = taskInfo.getMeasureValue(TaskMetric.EVENT_COVERAGE_QUANTILE);
+        return getIntensity(ratio, eventCoverage, eventCoverageQuantile);
+    }
+
+    /**
+     * 
+     */
+    static UsabilitySmellIntensity getIntensity(int ratio) {
+        return getIntensity(ratio, -1, -1);
+    }
+
+    /**
+     * 
+     */
+    static UsabilitySmellIntensity getIntensity(int ratio,
+                                                int eventCoverage,
+                                                int eventCoverageQuantile)
+    {
+        if ((ratio > 0) && ((eventCoverageQuantile == -1) || (eventCoverageQuantile > 0))) {
+            return new UsabilitySmellIntensity(ratio, eventCoverage, eventCoverageQuantile);
+        }
+        else {
+            return null;
+        }
+    }
+
+    /**
+     * <p>
+     * TODO: comment
+     * </p>
+     *
+     * @param ratio
+     * @param eventCoverage
+     * @param eventCoverageQuantile
+     */
+    private UsabilitySmellIntensity(int ratio, int eventCoverage, int eventCoverageQuantile) {
+        super();
+        this.ratio = ratio;
+        this.eventCoverage = eventCoverage;
+        this.eventCoverageQuantile = eventCoverageQuantile;
+    }
+
+    /**
+     * @return the ratio
+     */
+    public int getRatio() {
+        return ratio;
+    }
+
+    /**
+     * @return the eventCoverage
+     */
+    public int getEventCoverage() {
+        return eventCoverage;
+    }
+
+    /**
+     * @return the eventCoverageQuantile
+     */
+    public int getEventCoverageQuantile() {
+        return eventCoverageQuantile;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return Integer.toString(ratio);
+    }
+    
+}
