/*==========================================================================*\ | $Id: HierarchicalProgressTracker.java,v 1.1 2010/03/02 18:38:53 aallowat Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2009 Virginia Tech | | This file is part of Web-CAT Electronic Submitter. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU General Public License as published by | the Free Software Foundation; either version 2 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU General Public License along | with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.submitter.internal.utility; import java.util.Stack; //------------------------------------------------------------------------- /** * A class that allows a job to track its progress using a hierarchy of tasks. * The top-level task partitions the entire progress range (0-100%) into * subranges; if nested tasks are created by further calls to * {@link #beginTask}, then the current subrange is partitioned further, * allowing for easy management of subtasks. * * @author Tony Allevato * @author Last changed by $Author: aallowat $ * @version $Revision: 1.1 $ $Date: 2010/03/02 18:38:53 $ */ public class HierarchicalProgressTracker { //~ Constructors ................,,,....................................... // ---------------------------------------------------------- /** * Initializes a new hierarchical progress tracker. */ public HierarchicalProgressTracker() { tasks = new Stack<Task>(); isDone = false; } //~ Methods ............................................................... // ---------------------------------------------------------- /** * Subdivide the next unit of work into a subtask where the steps of the * subtask have the specified weights. * * @param description a description of the subtask * @param weights an array of integers describing how the steps of the * subtask are weighted */ public void beginTask(String description, int[] weights) { tasks.push(new Task(weights, weights.length, description)); } // ---------------------------------------------------------- /** * Subdivide the next unit of work into a subtask where the steps of the * subtask have equal weight. * * @param description a description of the subtask * @param totalWork the number of units of work that make up the subtask */ public void beginTask(String description, int totalWork) { tasks.push(new Task(null, totalWork, description)); } // ---------------------------------------------------------- /** * Subdivide the next unit of work into a subtask where the steps of the * subtask have the specified weights. * * @param weights an array of integers describing how the steps of the * subtask are weighted */ public void beginTask(int[] weights) { beginTask(null, weights); } // ---------------------------------------------------------- /** * Subdivide the next unit of work into a subtask where the steps of the * subtask have equal weight. * * @param totalWork the number of units of work that make up the subtask */ public void beginTask(int totalWork) { beginTask(null, totalWork); } // ---------------------------------------------------------- /** * Performs the specified number of units of work on the current subtask. * Note that if the accumulated number of units would meet or exceed the * total work for the task, the task must still be explicitly completed by * calling {@link #completeCurrentTask()}; this method does not * automatically complete such tasks. * * @param delta the number of units of work to perform */ public void worked(int delta) { Task task = tasks.peek(); task.step(delta); } // ---------------------------------------------------------- /** * Completes the current subtask. */ public void completeCurrentTask() { tasks.pop(); if (tasks.empty()) { setDone(); } else { worked(1); } } // ---------------------------------------------------------- /** * Gets the description of the current subtask. * * @return the description of the current subtask, or null if there is none */ public String descriptionOfCurrentTask() { int lastIndex = tasks.size() - 1; for (int i = lastIndex; i >= 0; i--) { Task task = tasks.get(i); if (task.description() != null) { return task.description(); } } return null; } // ---------------------------------------------------------- /** * Gets the percentage of total work done across all subtasks so far. * * @return the percentage of total work done, a floating-point value * between 0 and 1 */ public double percentDone() { if (isDone) { return 1; } double workDone = 0, divisor = 1; for (Task task : tasks) { workDone += task.percentDone() * divisor; divisor *= task.nextWeightPercent(); } return workDone; } // ---------------------------------------------------------- /** * Gets a value indicating whether the progress tracker is done. * * @return true if the tracker is done, otherwise false */ public boolean isDone() { return isDone; } // ---------------------------------------------------------- /** * Explicitly sets all work on the progress tracker to be done. */ public void setDone() { isDone = true; } //~ Private classes ....................................................... // ---------------------------------------------------------- /** * Represents a subtask in the progress tracking stack. */ private class Task { //~ Public Fields ..................................................... public int weightSoFar; public int stepsDoneSoFar; public int[] weights; public int totalWeight; public int totalSteps; //~ Constructors ...................................................... // ---------------------------------------------------------- public Task(int[] weights, int total, String desc) { initialize(weights, total); description = desc; } // ---------------------------------------------------------- private void initialize(int[] myWeights, int total) { weights = myWeights; totalWeight = 0; if (myWeights == null) { totalWeight = total; } else { for (int weight : myWeights) { totalWeight += weight; } } weightSoFar = 0; stepsDoneSoFar = 0; totalSteps = total; } //~ Public Methods .................................................... // ---------------------------------------------------------- public double nextWeightPercent() { if (weights == null) { return 1.0 / totalWeight; } else { return (double) weights[stepsDoneSoFar] / totalWeight; } } // ---------------------------------------------------------- public void step(int delta) { int stop = Math.min(totalSteps, stepsDoneSoFar + delta); if (weights == null) { weightSoFar += delta; } else { for (int i = stepsDoneSoFar; i < stop; i++) { weightSoFar += weights[i]; } } stepsDoneSoFar += delta; if (stepsDoneSoFar > totalSteps) { stepsDoneSoFar = totalSteps; weightSoFar = totalWeight; } } // ---------------------------------------------------------- public double percentDone() { return (double) weightSoFar / (double) totalWeight; } // ---------------------------------------------------------- public String description() { if (description != null) { return description; } else { return null; } } //~ Instance/static variables (for HierarchicalProgressTracker.Task) .. private String description; } //~ Instance/static variables (for HierarchicalProgressTracker) ........... private boolean isDone; private Stack<Task> tasks; }