View Javadoc
1   /*
2    * Copyright (C) 2008-2011, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.lib;
12  
13  import java.util.concurrent.Future;
14  import java.util.concurrent.TimeUnit;
15  
16  import org.eclipse.jgit.lib.internal.WorkQueue;
17  
18  /**
19   * ProgressMonitor that batches update events.
20   */
21  public abstract class BatchingProgressMonitor implements ProgressMonitor {
22  	private long delayStartTime;
23  
24  	private TimeUnit delayStartUnit = TimeUnit.MILLISECONDS;
25  
26  	private Task task;
27  
28  	/**
29  	 * Set an optional delay before the first output.
30  	 *
31  	 * @param time
32  	 *            how long to wait before output. If 0 output begins on the
33  	 *            first {@link #update(int)} call.
34  	 * @param unit
35  	 *            time unit of {@code time}.
36  	 */
37  	public void setDelayStart(long time, TimeUnit unit) {
38  		delayStartTime = time;
39  		delayStartUnit = unit;
40  	}
41  
42  	/** {@inheritDoc} */
43  	@Override
44  	public void start(int totalTasks) {
45  		// Ignore the number of tasks.
46  	}
47  
48  	/** {@inheritDoc} */
49  	@Override
50  	public void beginTask(String title, int work) {
51  		endTask();
52  		task = new Task(title, work);
53  		if (delayStartTime != 0)
54  			task.delay(delayStartTime, delayStartUnit);
55  	}
56  
57  	/** {@inheritDoc} */
58  	@Override
59  	public void update(int completed) {
60  		if (task != null)
61  			task.update(this, completed);
62  	}
63  
64  	/** {@inheritDoc} */
65  	@Override
66  	public void endTask() {
67  		if (task != null) {
68  			task.end(this);
69  			task = null;
70  		}
71  	}
72  
73  	/** {@inheritDoc} */
74  	@Override
75  	public boolean isCancelled() {
76  		return false;
77  	}
78  
79  	/**
80  	 * Update the progress monitor if the total work isn't known,
81  	 *
82  	 * @param taskName
83  	 *            name of the task.
84  	 * @param workCurr
85  	 *            number of units already completed.
86  	 */
87  	protected abstract void onUpdate(String taskName, int workCurr);
88  
89  	/**
90  	 * Finish the progress monitor when the total wasn't known in advance.
91  	 *
92  	 * @param taskName
93  	 *            name of the task.
94  	 * @param workCurr
95  	 *            total number of units processed.
96  	 */
97  	protected abstract void onEndTask(String taskName, int workCurr);
98  
99  	/**
100 	 * Update the progress monitor when the total is known in advance.
101 	 *
102 	 * @param taskName
103 	 *            name of the task.
104 	 * @param workCurr
105 	 *            number of units already completed.
106 	 * @param workTotal
107 	 *            estimated number of units to process.
108 	 * @param percentDone
109 	 *            {@code workCurr * 100 / workTotal}.
110 	 */
111 	protected abstract void onUpdate(String taskName, int workCurr,
112 			int workTotal, int percentDone);
113 
114 	/**
115 	 * Finish the progress monitor when the total is known in advance.
116 	 *
117 	 * @param taskName
118 	 *            name of the task.
119 	 * @param workCurr
120 	 *            total number of units processed.
121 	 * @param workTotal
122 	 *            estimated number of units to process.
123 	 * @param percentDone
124 	 *            {@code workCurr * 100 / workTotal}.
125 	 */
126 	protected abstract void onEndTask(String taskName, int workCurr,
127 			int workTotal, int percentDone);
128 
129 	private static class Task implements Runnable {
130 		/** Title of the current task. */
131 		private final String taskName;
132 
133 		/** Number of work units, or {@link ProgressMonitor#UNKNOWN}. */
134 		private final int totalWork;
135 
136 		/** True when timer expires and output should occur on next update. */
137 		private volatile boolean display;
138 
139 		/** Scheduled timer, supporting cancellation if task ends early. */
140 		private Future<?> timerFuture;
141 
142 		/** True if the task has displayed anything. */
143 		private boolean output;
144 
145 		/** Number of work units already completed. */
146 		private int lastWork;
147 
148 		/** Percentage of {@link #totalWork} that is done. */
149 		private int lastPercent;
150 
151 		Task(String taskName, int totalWork) {
152 			this.taskName = taskName;
153 			this.totalWork = totalWork;
154 			this.display = true;
155 		}
156 
157 		void delay(long time, TimeUnit unit) {
158 			display = false;
159 			timerFuture = WorkQueue.getExecutor().schedule(this, time, unit);
160 		}
161 
162 		@Override
163 		public void run() {
164 			display = true;
165 		}
166 
167 		void update(BatchingProgressMonitor pm, int completed) {
168 			lastWork += completed;
169 
170 			if (totalWork == UNKNOWN) {
171 				// Only display once per second, as the alarm fires.
172 				if (display) {
173 					pm.onUpdate(taskName, lastWork);
174 					output = true;
175 					restartTimer();
176 				}
177 			} else {
178 				// Display once per second or when 1% is done.
179 				int currPercent = lastWork * 100 / totalWork;
180 				if (display) {
181 					pm.onUpdate(taskName, lastWork, totalWork, currPercent);
182 					output = true;
183 					restartTimer();
184 					lastPercent = currPercent;
185 				} else if (currPercent != lastPercent) {
186 					pm.onUpdate(taskName, lastWork, totalWork, currPercent);
187 					output = true;
188 					lastPercent = currPercent;
189 				}
190 			}
191 		}
192 
193 		private void restartTimer() {
194 			display = false;
195 			timerFuture = WorkQueue.getExecutor().schedule(this, 1,
196 					TimeUnit.SECONDS);
197 		}
198 
199 		void end(BatchingProgressMonitor pm) {
200 			if (output) {
201 				if (totalWork == UNKNOWN) {
202 					pm.onEndTask(taskName, lastWork);
203 				} else {
204 					int pDone = lastWork * 100 / totalWork;
205 					pm.onEndTask(taskName, lastWork, totalWork, pDone);
206 				}
207 			}
208 			if (timerFuture != null)
209 				timerFuture.cancel(false /* no interrupt */);
210 		}
211 	}
212 }