// 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.tasktrees.treeimpl; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship; import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional; import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance; 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.ITaskInstanceList; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo; import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric; /** *

* this is the default implementation of the interface {@link ITaskModel}. It * does not do anything fancy except implementing the interface. It also calculates on * initialisations the measures for diverse metrics of the task belonging to the model *

* * @author Patrick Harms */ class TaskModel implements ITaskModel { /** *

* default serial version UID *

*/ private static final long serialVersionUID = 1L; /** *

* all metrics calculated by this type of task model *

*/ private static final TaskMetric[] taskMetrics = new TaskMetric[] { TaskMetric.COUNT, TaskMetric.DEPTH, TaskMetric.EVENT_COVERAGE, TaskMetric.EVENT_COVERAGE_RATIO, TaskMetric.EVENT_COVERAGE_QUANTILE }; /** *

* the user sessions belonging to the model *

*/ private List userSessions; /** *

* index for effectively accessing the model and calculating statistics about it *

*/ private transient TaskModelIndex index = null; /** *

* initializes the task model with the user sessions out of which the tasks are extracted *

* * @param userSessions as described */ TaskModel(List userSessions) { if ((userSessions == null) || (userSessions.size() == 0)) { throw new IllegalArgumentException("user sessions must not be null"); } this.userSessions = userSessions; } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel#getUserSessions() */ @Override public List getUserSessions() { ensureInitialized(); return Collections.unmodifiableList(userSessions); } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel#getTasks() */ @Override public Collection getTasks() { ensureInitialized(); return Collections.unmodifiableCollection(index.taskMap.keySet()); } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel#getTaskInfo(ITask) */ @Override public ITaskInfo getTaskInfo(ITask task) { ensureInitialized(); return index.taskMap.get(task); } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel#getAllMetrics() */ @Override public TaskMetric[] getAllMetrics() { return taskMetrics; } /* (non-Javadoc) * @see java.lang.Object#clone() */ @Override public TaskModel clone() { return new TaskModel(userSessions); } /** *

* internal convenience method that initializes the internal index and calculates all measures * for metrics available for the tasks *

*/ private synchronized void ensureInitialized() { if (index == null) { index = new TaskModelIndex(); for (IUserSession session : this.userSessions) { for (ITaskInstance taskInstance : session) { index.handleTaskInstance(taskInstance, null); } } // count all events covered int allEventsCovered = 0; Collection tasks = getTasks(); for (ITask task : tasks) { if (task instanceof IEventTask) { allEventsCovered += task.getInstances().size(); } } int[] eventCoverageRatios = new int[tasks.size()]; int i = 0; // add some further measures for (ITask task : tasks) { TaskInfo info = index.taskMap.get(task); info.addMeasure(TaskMetric.EVENT_COVERAGE_RATIO); int coveredEvents = info.getMeasureValue(TaskMetric.EVENT_COVERAGE); int coverageRatio = 0; if (allEventsCovered > 0) { coverageRatio = (coveredEvents * 1000) / allEventsCovered; } eventCoverageRatios[i++] = coverageRatio; info.setCount(TaskMetric.EVENT_COVERAGE_RATIO, null, coverageRatio); } Arrays.sort(eventCoverageRatios); // add some further measures for (ITask task : tasks) { TaskInfo info = index.taskMap.get(task); info.addMeasure(TaskMetric.EVENT_COVERAGE_QUANTILE); int quantile = Arrays.binarySearch (eventCoverageRatios, info.getMeasureValue(TaskMetric.EVENT_COVERAGE_RATIO)); quantile = 1000 * quantile / eventCoverageRatios.length; info.setCount(TaskMetric.EVENT_COVERAGE_QUANTILE, null, quantile); } //index.dumpToCSV(System.out); /*try { OutputStream stream = new FileOutputStream(new File("tasks.csv")); index.dumpToCSV(new PrintStream(stream)); stream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); }*/ } } /** *

* the index of task infos used internally. The index is created once and while that filled * with task infos for each observed task containing all measures for metrics belonging * to the tasks. *

* * @author Patrick Harms */ private static class TaskModelIndex { /** *

* the tasks contained in the user session belonging to the model as well as statistical * infos about them *

*/ private Map taskMap = new HashMap(); /** *

* called on initialization to fill the index with infos about the given task instance * as well as to calculate the appropriate metrics *

*/ private int[] handleTaskInstance(ITaskInstance taskInstance, ITask context) { int eventTaskInstancesCovered = 0; int depth = 0; if (taskInstance instanceof ITaskInstanceList) { for (ITaskInstance child : (ITaskInstanceList) taskInstance) { int[] measures = handleTaskInstance(child, taskInstance.getTask()); eventTaskInstancesCovered += measures[0]; depth = Math.max(depth, measures[1]); } if ((((ITaskInstanceList) taskInstance).size() == 0) && (taskInstance instanceof IIterationInstance)) { // ensure also empty task infos for unselected variants ensureTaskInfo(((IIteration) taskInstance.getTask()).getMarkedTask(), context); } } else if (taskInstance instanceof ISelectionInstance) { ITaskInstance child = ((ISelectionInstance) taskInstance).getChild(); int[] measures = handleTaskInstance(child, taskInstance.getTask()); eventTaskInstancesCovered += measures[0]; depth = Math.max(depth, measures[1]); // ensure also empty task infos for unselected variants for (ITask otherChildTask : ((ISelection) taskInstance.getTask()).getChildren()) { ensureTaskInfo(otherChildTask, context); } } else if (taskInstance instanceof IOptionalInstance) { ITaskInstance child = ((IOptionalInstance) taskInstance).getChild(); if (child != null) { int[] measures = handleTaskInstance(child, taskInstance.getTask()); eventTaskInstancesCovered += measures[0]; depth = Math.max(depth, measures[1]); } else { // ensure also empty task infos for unselected variants ensureTaskInfo(((IOptional) taskInstance.getTask()).getMarkedTask(), context); } } else if (taskInstance instanceof IEventTaskInstance) { eventTaskInstancesCovered = 1; } depth++; ensureTaskInfo(taskInstance.getTask(), context, eventTaskInstancesCovered, depth); return new int[] { eventTaskInstancesCovered, depth }; } /** *

* internal convenience method to build the task model during initialization *

*/ private void ensureTaskInfo(ITask task, ITask context) { ensureTaskInfo(task, context, 0, 0); if (task instanceof IStructuringTemporalRelationship) { for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) { ensureTaskInfo(child, task); } } else if (task instanceof IMarkingTemporalRelationship) { ensureTaskInfo(((IMarkingTemporalRelationship) task).getMarkedTask(), task); } } /** *

* internal convenience method to build the task model during initialization. Adds a new * task info object to the map for the provided task and fills it with measures. If there * are already some task infos for the task, the contained measures are updated according * to the parameters. *

*/ private void ensureTaskInfo(ITask task, ITask context, int eventTaskInstancesCovered, int depth) { TaskInfo taskInfo = taskMap.get(task); if (taskInfo == null) { taskInfo = new TaskInfo(task); taskInfo.addMeasure(TaskMetric.COUNT); taskInfo.addMeasure(TaskMetric.EVENT_COVERAGE); taskInfo.addMeasure(TaskMetric.DEPTH); taskMap.put(task, taskInfo); taskInfo.setCount(TaskMetric.DEPTH, null, getDepth(task)); } taskInfo.increaseCount(TaskMetric.COUNT, context, 1); taskInfo.increaseCount(TaskMetric.EVENT_COVERAGE, context, eventTaskInstancesCovered); taskInfo.setCount(TaskMetric.DEPTH, context, depth); } /** *

* internal convenience method to calculate the maximum depth of a task *

*/ private int getDepth(ITask task) { if (task instanceof IMarkingTemporalRelationship) { return getDepth(((IMarkingTemporalRelationship) task).getMarkedTask()) + 1; } else if (task instanceof IStructuringTemporalRelationship) { int maxDepth = 0; for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) { maxDepth = Math.max(maxDepth, getDepth(child)); } return maxDepth + 1; } else { // event tasks return 1; } } /** * */ /*private void dumpToCSV(PrintStream out) { out.println("taskid;depth;count;eventcoverage;eventcoverageratio"); for (Map.Entry entry : taskMap.entrySet()) { out.print(entry.getKey().getId()); out.print(';'); out.print(entry.getValue().getMeasureValue(TaskMetric.DEPTH)); out.print(';'); out.print(entry.getValue().getMeasureValue(TaskMetric.COUNT)); out.print(';'); out.print(entry.getValue().getMeasureValue(TaskMetric.EVENT_COVERAGE)); out.print(';'); out.print(entry.getValue().getMeasureValue(TaskMetric.EVENT_COVERAGE_RATIO)); out.println(); } }*/ } }