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