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 @Override 72 public void start(int totalTasks) { 73 // Ignore the number of tasks. 74 } 75 76 @Override 77 public void beginTask(String title, int work) { 78 endTask(); 79 task = new Task(title, work); 80 if (delayStartTime != 0) 81 task.delay(delayStartTime, delayStartUnit); 82 } 83 84 @Override 85 public void update(int completed) { 86 if (task != null) 87 task.update(this, completed); 88 } 89 90 @Override 91 public void endTask() { 92 if (task != null) { 93 task.end(this); 94 task = null; 95 } 96 } 97 98 @Override 99 public boolean isCancelled() { 100 return false; 101 } 102 103 /** 104 * Update the progress monitor if the total work isn't known, 105 * 106 * @param taskName 107 * name of the task. 108 * @param workCurr 109 * number of units already completed. 110 */ 111 protected abstract void onUpdate(String taskName, int workCurr); 112 113 /** 114 * Finish the progress monitor when the total wasn't known in advance. 115 * 116 * @param taskName 117 * name of the task. 118 * @param workCurr 119 * total number of units processed. 120 */ 121 protected abstract void onEndTask(String taskName, int workCurr); 122 123 /** 124 * Update the progress monitor when the total is known in advance. 125 * 126 * @param taskName 127 * name of the task. 128 * @param workCurr 129 * number of units already completed. 130 * @param workTotal 131 * estimated number of units to process. 132 * @param percentDone 133 * {@code workCurr * 100 / workTotal}. 134 */ 135 protected abstract void onUpdate(String taskName, int workCurr, 136 int workTotal, int percentDone); 137 138 /** 139 * Finish the progress monitor when the total is known in advance. 140 * 141 * @param taskName 142 * name of the task. 143 * @param workCurr 144 * total number of units processed. 145 * @param workTotal 146 * estimated number of units to process. 147 * @param percentDone 148 * {@code workCurr * 100 / workTotal}. 149 */ 150 protected abstract void onEndTask(String taskName, int workCurr, 151 int workTotal, int percentDone); 152 153 private static class Task implements Runnable { 154 /** Title of the current task. */ 155 private final String taskName; 156 157 /** Number of work units, or {@link ProgressMonitor#UNKNOWN}. */ 158 private final int totalWork; 159 160 /** True when timer expires and output should occur on next update. */ 161 private volatile boolean display; 162 163 /** Scheduled timer, supporting cancellation if task ends early. */ 164 private Future<?> timerFuture; 165 166 /** True if the task has displayed anything. */ 167 private boolean output; 168 169 /** Number of work units already completed. */ 170 private int lastWork; 171 172 /** Percentage of {@link #totalWork} that is done. */ 173 private int lastPercent; 174 175 Task(String taskName, int totalWork) { 176 this.taskName = taskName; 177 this.totalWork = totalWork; 178 this.display = true; 179 } 180 181 void delay(long time, TimeUnit unit) { 182 display = false; 183 timerFuture = WorkQueue.getExecutor().schedule(this, time, unit); 184 } 185 186 @Override 187 public void run() { 188 display = true; 189 } 190 191 void update(BatchingProgressMonitor pm, int completed) { 192 lastWork += completed; 193 194 if (totalWork == UNKNOWN) { 195 // Only display once per second, as the alarm fires. 196 if (display) { 197 pm.onUpdate(taskName, lastWork); 198 output = true; 199 restartTimer(); 200 } 201 } else { 202 // Display once per second or when 1% is done. 203 int currPercent = lastWork * 100 / totalWork; 204 if (display) { 205 pm.onUpdate(taskName, lastWork, totalWork, currPercent); 206 output = true; 207 restartTimer(); 208 lastPercent = currPercent; 209 } else if (currPercent != lastPercent) { 210 pm.onUpdate(taskName, lastWork, totalWork, currPercent); 211 output = true; 212 lastPercent = currPercent; 213 } 214 } 215 } 216 217 private void restartTimer() { 218 display = false; 219 timerFuture = WorkQueue.getExecutor().schedule(this, 1, 220 TimeUnit.SECONDS); 221 } 222 223 void end(BatchingProgressMonitor pm) { 224 if (output) { 225 if (totalWork == UNKNOWN) { 226 pm.onEndTask(taskName, lastWork); 227 } else { 228 int pDone = lastWork * 100 / totalWork; 229 pm.onEndTask(taskName, lastWork, totalWork, pDone); 230 } 231 } 232 if (timerFuture != null) 233 timerFuture.cancel(false /* no interrupt */); 234 } 235 } 236 }