/*==========================================================================*\ | $Id: HierarchicalProgressTracker.java,v 1.1 2010/05/11 14:51:44 aallowat Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2008-2009 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 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 Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.jobqueue; import java.util.Enumeration; import org.apache.log4j.Logger; import com.webobjects.foundation.NSMutableArray; //------------------------------------------------------------------------- /** * 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/05/11 14:51:44 $ */ public class HierarchicalProgressTracker { //~ Constructor ....................................................... // ---------------------------------------------------------- public HierarchicalProgressTracker() { tasks = new NSMutableArray<Task>(); isDone = false; } //~ Public Methods .................................................... // ---------------------------------------------------------- public void beginTask(String description, int[] weights) { tasks.addObject(new Task(weights, weights.length, description)); if (description == null) { logTaskStack("New task with " + weights.length + " units"); } else { logTaskStack("New task (\"" + description + "\") with " + weights.length + " units"); } } // ---------------------------------------------------------- public void beginTask(String description, int totalWork) { tasks.addObject(new Task(null, totalWork, description)); if (description == null) { logTaskStack("New task with " + totalWork + " units"); } else { logTaskStack("New task (\"" + description + "\") with " + totalWork + " units"); } } // ---------------------------------------------------------- public void beginTask(int[] weights) { beginTask(null, weights); } // ---------------------------------------------------------- public void beginTask(int totalWork) { beginTask(null, totalWork); } // ---------------------------------------------------------- public void worked(int delta) { Task task = tasks.lastObject(); task.step(delta); } // ---------------------------------------------------------- public void completeCurrentTask() { tasks.removeLastObject(); if (tasks.count() == 0) { setIsDone(true); } else { worked(1); } logTaskStack("Task " + tasks.count() + " complete"); } // ---------------------------------------------------------- public String descriptionOfCurrentTask() { int lastIndex = tasks.count() - 1; for (int i = lastIndex; i >= 0; i--) { Task task = tasks.objectAtIndex(i); if (task.description() != null) { return task.description(); } } return null; } // ---------------------------------------------------------- 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; } // ---------------------------------------------------------- public boolean isDone() { return isDone; } // ---------------------------------------------------------- public void setIsDone(boolean done) { isDone = done; } //~ Private Methods/Classes ........................................... // ---------------------------------------------------------- private void logTaskStack(String prefix) { if (!log.isDebugEnabled()) return; StringBuffer buffer = new StringBuffer(); buffer.append(prefix); buffer.append(": "); Enumeration<Task> e = tasks.objectEnumerator(); while (e.hasMoreElements()) { Task task = e.nextElement(); buffer.append("("); buffer.append(task.stepsDoneSoFar); buffer.append(" of "); buffer.append(task.totalSteps); buffer.append(") "); } log.debug(buffer.toString()); } // ---------------------------------------------------------- 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 NSMutableArray<Task> tasks; private static Logger log = Logger.getLogger( HierarchicalProgressTracker.class); }