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 }