Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDremoveEventDuplicates.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDremoveEventDuplicates.java	(revision 2260)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/sequences/CMDremoveEventDuplicates.java	(revision 2260)
@@ -0,0 +1,290 @@
+//   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.commands.sequences;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+import de.ugoe.cs.autoquest.SequenceInstanceOf;
+import de.ugoe.cs.autoquest.eventcore.Event;
+import de.ugoe.cs.autoquest.eventcore.IHierarchicalEventTarget;
+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.MouseDoubleClick;
+import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection;
+import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDremoveEventDuplicates implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "removeEventDuplicates <sequences> {<new sequences>}";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run(List<Object> parameters) {
+        String sequencesName;
+        String newSequencesName;
+        try {
+            sequencesName = (String) parameters.get(0);
+            if (parameters.size() > 1) {
+                newSequencesName = (String) parameters.get(1);
+            }
+            else {
+                newSequencesName = sequencesName;
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("must provide a sequences name");
+        }
+
+        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
+        if (dataObject == null) {
+            CommandHelpers.objectNotFoundMessage(sequencesName);
+            return;
+        }
+        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
+            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
+            return;
+        }
+
+        Collection<List<Event>> newSequences =
+            removeEventDuplicates((Collection<List<Event>>) dataObject);
+
+        if (GlobalDataContainer.getInstance().addData(newSequencesName, newSequences)) {
+            CommandHelpers.dataOverwritten(newSequencesName);
+        }
+        
+    }
+
+    /**
+     *
+     */
+    private Collection<List<Event>> removeEventDuplicates(Collection<List<Event>> sequences) {
+        Collection<List<Event>> newSequences = new LinkedList<List<Event>>();
+        
+        //long[] diffCounter = new long[1000];
+        //Map<String, long[]> diffCounterForPairs = new HashMap<>();
+        
+        int eventsRemoved = 0;
+        
+        for (List<Event> sequence : sequences) {
+            Event predecessor = null;
+            List<Event> result = new ArrayList<Event>();
+            
+            System.out.println("sequence");
+            
+            for (Event event : sequence) {
+                long diff = (predecessor != null) ?
+                    (event.getTimestamp() - predecessor.getTimestamp()) : Long.MAX_VALUE;
+                
+                System.out.println("  " + event.getType() + "\t" + ((IHierarchicalEventTarget) event.getTarget()).getSpecification().toString() + "\t" + event.getTimestamp() + "\t" + diff);
+                /*if (diff < diffCounter.length) {
+                    diffCounter[(int) diff]++;
+                    
+                    long[] specCounter = diffCounterForPairs.get(getKey(predecessor, event));
+                    
+                    if (specCounter == null) {
+                        specCounter = new long[diffCounter.length];
+                        diffCounterForPairs.put(getKey(predecessor, event), specCounter);
+                    }
+                    
+                    specCounter[(int) diff]++;
+                }*/
+                
+                // this border of 30 milliseconds was determined by plotting the time differences
+                // and seeing that there is a real drop of time differences above 30 milliseconds.
+                // And 30 milliseconds is still small enough to expect, that this is not triggered
+                // by a human.
+                if (diff < 30) {
+                    Event toPrefer = getEventToPrefer(predecessor, event);
+
+                    if (toPrefer != null) {
+                        result.set(result.size() - 1, toPrefer);
+                        eventsRemoved++;
+                    }
+                    else {
+                        // do not prefer any of them
+                        result.add(event);
+                    }
+                }
+                else {
+                    result.add(event);
+                }
+                
+                predecessor = result.get(result.size() - 1);
+            }
+            
+            newSequences.add(result);
+        }
+        
+        /*for (int i = 0; i < diffCounter.length; i++) {
+            System.out.print((i + 1) + "ms :\t ");
+            for (int j = 0; j < diffCounter[i]; j = j + 10) {
+                System.out.print('|');
+            }
+            
+            System.out.println();
+        }
+        
+        for (Map.Entry<String, long[]> entry : diffCounterForPairs.entrySet()) {
+            System.out.println("\n\n" + entry.getKey());
+            for (int i = 0; i < entry.getValue().length; i++) {
+                if (entry.getValue()[i] > 0) {
+                    System.out.print((i + 1) + "ms :\t ");
+                    for (int j = 0; j < entry.getValue()[i]; j = j + 10) {
+                        System.out.print('|');
+                    }
+                    
+                    System.out.println();
+                }
+            }
+        }*/
+        
+        Console.println("removed " + eventsRemoved + " events");
+        
+        return newSequences;
+    }
+
+    /**
+     *
+     */
+    /*private String getKey(Event event1, Event event2) {
+        return event1.getType().getClass().getSimpleName() + " on " + event1.getTarget().getClass()
+            + "  -->  " + event2.getType().getClass().getSimpleName() + " on " +
+            event2.getTarget().getClass();
+    }*/
+
+    /**
+     *
+     */
+    private Event getEventToPrefer(Event event1, Event event2) {
+        Event result = null;
+        
+        if (!isSameEventType(event1, event2)) {
+            if (isSameEventTarget(event1, event2) ||
+                oneIsParentTargetOfOtherTarget(event1, event2))
+            {
+                // in a combination of click and value selection on the same element or an element
+                // and its parent, prefer the value selection as it is triggered by the click
+                if (isLeftMouseButtonInteraction(event1) && isValueSelection(event2)) {
+                    result = event2;
+                }
+                else if (isLeftMouseButtonInteraction(event2) && isValueSelection(event1)) {
+                    result = event1;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     *
+     */
+    private boolean isSameEventType(Event event1, Event event2) {
+        return event1.getType().getName().equals(event2.getType().getName());
+    }
+
+    /**
+     * 
+     */
+    private boolean isSameEventTarget(Event event1, Event event2) {
+        return event1.getTarget().equals(event2.getTarget());
+    }
+
+    /**
+     *
+     */
+    private boolean oneIsParentTargetOfOtherTarget(Event event1, Event event2) {
+        if ((event1.getTarget() instanceof IGUIElement) &&
+            (event2.getTarget() instanceof IGUIElement))
+        {
+            IGUIElement elem1 = (IGUIElement) event1.getTarget();
+            IGUIElement elem2 = (IGUIElement) event2.getTarget();
+            
+            return isParent(elem1, elem2) || isParent(elem2, elem1);
+        }
+        
+        return false;
+    }
+
+    /**
+     *
+     */
+    private boolean isParent(IGUIElement elem1, IGUIElement elem2) {
+        IGUIElement parent = elem2.getParent();
+        
+        while (parent != null) {
+            if (parent.equals(elem1)) {
+                return true;
+            }
+            
+            parent = parent.getParent();
+        }
+        
+        return false;
+    }
+
+    /**
+     * 
+     */
+    private boolean isLeftMouseButtonInteraction(Event event) {
+        if (((event.getType() instanceof MouseClick) ||
+             (event.getType() instanceof MouseDoubleClick) ||
+             (event.getType() instanceof MouseButtonDown)) &&
+            (((MouseButtonInteraction) event.getType()).getButton() ==
+                MouseButtonInteraction.Button.LEFT))
+        {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * 
+     */
+    private boolean isValueSelection(Event event) {
+        return event.getType() instanceof ValueSelection;
+    }
+}
Index: trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDusabilityAutoTagger.java
===================================================================
--- trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDusabilityAutoTagger.java	(revision 2260)
+++ trunk/autoquest-ui-core/src/main/java/de/ugoe/cs/autoquest/commands/usability/CMDusabilityAutoTagger.java	(revision 2260)
@@ -0,0 +1,183 @@
+//   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.commands.usability;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import de.ugoe.cs.autoquest.CommandHelpers;
+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.usability.UsabilityEvaluationResult;
+import de.ugoe.cs.autoquest.usability.UsabilitySmell;
+import de.ugoe.cs.autoquest.usability.UsabilitySmell.ManualLabel;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.GlobalDataContainer;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDusabilityAutoTagger implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        List<String> usabilityResultNames = new ArrayList<>(parameters.size());
+        try {
+            for (Object parameter : parameters) {
+                usabilityResultNames.add((String) parameter);
+            }
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+
+        List<UsabilityEvaluationResult> usabilityResults = new ArrayList<>(usabilityResultNames.size());
+        
+        for (String usabilityResultName : usabilityResultNames) {
+            Object dataObject = GlobalDataContainer.getInstance().getData(usabilityResultName);
+            if (dataObject == null) {
+                CommandHelpers.objectNotFoundMessage(usabilityResultName);
+                return;
+            }
+            if (!(dataObject instanceof UsabilityEvaluationResult)) {
+                CommandHelpers.objectNotType(usabilityResultName, "UsabilityEvaluationResult");
+                return;
+            }
+            
+            usabilityResults.add((UsabilityEvaluationResult) dataObject);
+        }
+        
+        for (UsabilityEvaluationResult result : usabilityResults) {
+            for (UsabilitySmell smell : result.getAllSmells()) {
+                if (smell.getSmellingTask() != null) {
+                    smell.setManualLabel(ManualLabel.TRUE_POSITIVE);
+                    
+                    final Set<String> usedObjectNames = new HashSet<>();
+                    
+                    smell.getSmellingTask().accept(new DefaultTaskTraversingVisitor() {
+                       @Override
+                        public void visit(IEventTask eventTask) {
+                           usedObjectNames.add(((IEventTaskInstance) eventTask.getInstances().iterator().next()).getEvent().getTarget().toString()); 
+                        }
+                    });
+                    
+                    for (String tag : new LinkedList<String>(smell.getTags())) {
+                        smell.removeTag(tag);
+                    }
+                    
+                    if (usedObjectNames.remove("Cup")) {
+                        if (!smell.getTags().contains("Cup")) {
+                            smell.addTag("Cup");
+                        }
+                    }
+                    if (usedObjectNames.remove("StrongCoffee")) {
+                        if (!smell.getTags().contains("Strong Coffee")) {
+                            smell.addTag("Strong Coffee");
+                        }
+                        if (!smell.getTags().contains("Right Button")) {
+                            smell.addTag("Right Button");
+                        }
+                    }
+                    if (usedObjectNames.remove("LightCoffee")) {
+                        if (!smell.getTags().contains("Light Coffee")) {
+                            smell.addTag("Light Coffee");
+                        }
+                        if (!smell.getTags().contains("Right Button")) {
+                            smell.addTag("Right Button");
+                        }
+                    }
+                    if (usedObjectNames.remove("TwoLightCoffees")) {
+                        if (!smell.getTags().contains("Two Light Coffees")) {
+                            smell.addTag("Two Light Coffees");
+                        }
+                        if (!smell.getTags().contains("Right Button")) {
+                            smell.addTag("Right Button");
+                        }
+                    }
+                    if (usedObjectNames.remove("ButtonCopy")) {
+                        if (!smell.getTags().contains("Copy Button")) {
+                            smell.addTag("Copy Button");
+                        }
+                        if (!smell.getTags().contains("Right Button")) {
+                            smell.addTag("Right Button");
+                        }
+                    }
+                    if (usedObjectNames.remove("PrinterTop")) {
+                        if (!smell.getTags().contains("Printer Top")) {
+                            smell.addTag("Printer Top");
+                        }
+                    }
+                    if (usedObjectNames.remove("Paper")) {
+                        if (!smell.getTags().contains("Paper")) {
+                            smell.addTag("Paper");
+                        }
+                    }
+                    if (usedObjectNames.remove("Paper(Clone)1")) {
+                        if (!smell.getTags().contains("Paper")) {
+                            smell.addTag("Paper");
+                        }
+                    }
+                    if (usedObjectNames.remove("Paper(Clone)2")) {
+                        if (!smell.getTags().contains("Paper")) {
+                            smell.addTag("Paper");
+                        }
+                    }
+                    if (usedObjectNames.remove("Buzzer")) {
+                        if (!smell.getTags().contains("Reset")) {
+                            smell.addTag("Reset");
+                        }
+                    }
+                    if (usedObjectNames.remove("Camera (eye)")) {
+                        // ignore
+                    }
+                    if (usedObjectNames.remove("CenterEyeAnchor")) {
+                        // ignore
+                    }
+                    
+                    if (usedObjectNames.size() > 0) {
+                        if (!smell.getTags().contains("Wrong Button")) {
+                            smell.addTag("Wrong Button");
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "eventStatistics [<sequencesName>]*";
+    }
+
+}
