1 /* 2 * Copyright (C) 2008-2011, Google Inc. 3 * and other copyright owners as documented in the project's IP log. 4 * 5 * This program and the accompanying materials are made available 6 * under the terms of the Eclipse Distribution License v1.0 which 7 * accompanies this distribution, is reproduced below, and is 8 * available at http://www.eclipse.org/org/documents/edl-v10.php 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials provided 22 * with the distribution. 23 * 24 * - Neither the name of the Eclipse Foundation, Inc. nor the 25 * names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior 27 * written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 44 package org.eclipse.jgit.lib; 45 46 import java.util.concurrent.Future; 47 import java.util.concurrent.TimeUnit; 48 49 /** ProgressMonitor that batches update events. */ 50 public abstract class BatchingProgressMonitor implements ProgressMonitor { 51 private long delayStartTime; 52 53 private TimeUnit delayStartUnit = TimeUnit.MILLISECONDS; 54 55 private Task task; 56 57 /** 58 * Set an optional delay before the first output. 59 * 60 * @param time 61 * how long to wait before output. If 0 output begins on the 62 * first {@link #update(int)} call. 63 * @param unit 64 * time unit of {@code time}. 65 */ 66 public void setDelayStart(long time, TimeUnit unit) { 67 delayStartTime = time; 68 delayStartUnit = unit; 69 } 70 71 public void start(int totalTasks) { 72 // Ignore the number of tasks. 73 } 74 75 public void beginTask(String title, int work) { 76 endTask(); 77 task = new Task(title, work); 78 if (delayStartTime != 0) 79 task.delay(delayStartTime, delayStartUnit); 80 } 81 82 public void update(int completed) { 83 if (task != null) 84 task.update(this, completed); 85 } 86 87 public void endTask() { 88 if (task != null) { 89 task.end(this); 90 task = null; 91 } 92 } 93 94 public boolean isCancelled() { 95 return false; 96 } 97 98 /** 99 * Update the progress monitor if the total work isn't known, 100 * 101 * @param taskName 102 * name of the task. 103 * @param workCurr 104 * number of units already completed. 105 */ 106 protected abstract void onUpdate(String taskName, int workCurr); 107 108 /** 109 * Finish the progress monitor when the total wasn't known in advance. 110 * 111 * @param taskName 112 * name of the task. 113 * @param workCurr 114 * total number of units processed. 115 */ 116 protected abstract void onEndTask(String taskName, int workCurr); 117 118 /** 119 * Update the progress monitor when the total is known in advance. 120 * 121 * @param taskName 122 * name of the task. 123 * @param workCurr 124 * number of units already completed. 125 * @param workTotal 126 * estimated number of units to process. 127 * @param percentDone 128 * {@code workCurr * 100 / workTotal}. 129 */ 130 protected abstract void onUpdate(String taskName, int workCurr, 131 int workTotal, int percentDone); 132 133 /** 134 * Finish the progress monitor when the total is known in advance. 135 * 136 * @param taskName 137 * name of the task. 138 * @param workCurr 139 * total number of units processed. 140 * @param workTotal 141 * estimated number of units to process. 142 * @param percentDone 143 * {@code workCurr * 100 / workTotal}. 144 */ 145 protected abstract void onEndTask(String taskName, int workCurr, 146 int workTotal, int percentDone); 147 148 private static class Task implements Runnable { 149 /** Title of the current task. */ 150 private final String taskName; 151 152 /** Number of work units, or {@link ProgressMonitor#UNKNOWN}. */ 153 private final int totalWork; 154 155 /** True when timer expires and output should occur on next update. */ 156 private volatile boolean display; 157 158 /** Scheduled timer, supporting cancellation if task ends early. */ 159 private Future<?> timerFuture; 160 161 /** True if the task has displayed anything. */ 162 private boolean output; 163 164 /** Number of work units already completed. */ 165 private int lastWork; 166 167 /** Percentage of {@link #totalWork} that is done. */ 168 private int lastPercent; 169 170 Task(String taskName, int totalWork) { 171 this.taskName = taskName; 172 this.totalWork = totalWork; 173 this.display = true; 174 } 175 176 void delay(long time, TimeUnit unit) { 177 display = false; 178 timerFuture = WorkQueue.getExecutor().schedule(this, time, unit); 179 } 180 181 public void run() { 182 display = true; 183 } 184 185 void update(BatchingProgressMonitor pm, int completed) { 186 lastWork += completed; 187 188 if (totalWork == UNKNOWN) { 189 // Only display once per second, as the alarm fires. 190 if (display) { 191 pm.onUpdate(taskName, lastWork); 192 output = true; 193 restartTimer(); 194 } 195 } else { 196 // Display once per second or when 1% is done. 197 int currPercent = lastWork * 100 / totalWork; 198 if (display) { 199 pm.onUpdate(taskName, lastWork, totalWork, currPercent); 200 output = true; 201 restartTimer(); 202 lastPercent = currPercent; 203 } else if (currPercent != lastPercent) { 204 pm.onUpdate(taskName, lastWork, totalWork, currPercent); 205 output = true; 206 lastPercent = currPercent; 207 } 208 } 209 } 210 211 private void restartTimer() { 212 display = false; 213 timerFuture = WorkQueue.getExecutor().schedule(this, 1, 214 TimeUnit.SECONDS); 215 } 216 217 void end(BatchingProgressMonitor pm) { 218 if (output) { 219 if (totalWork == UNKNOWN) { 220 pm.onEndTask(taskName, lastWork); 221 } else { 222 int pDone = lastWork * 100 / totalWork; 223 pm.onEndTask(taskName, lastWork, totalWork, pDone); 224 } 225 } 226 if (timerFuture != null) 227 timerFuture.cancel(false /* no interrupt */); 228 } 229 } 230 }