// 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 smellingTasks = getDataEntryMethodChangeRatios(taskModel.getTasks()); analyzeDataEntryMethodChangeRatios(smellingTasks, results, taskModel); return results; } /** * */ private void analyzeDataEntryMethodChangeRatios(Map smellingTasks, UsabilityEvaluationResult results, ITaskModel taskModel) { for (Map.Entry 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 parameters = new HashMap(); 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 getDataEntryMethodChangeRatios(Collection tasks) { Map impatienceRatios = new HashMap(); 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 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); } }