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 }