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