| /* |
| * Copyright (C) 2008-2011, Google Inc. and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| package org.eclipse.jgit.lib; |
| |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.eclipse.jgit.lib.internal.WorkQueue; |
| |
| /** |
| * ProgressMonitor that batches update events. |
| */ |
| public abstract class BatchingProgressMonitor implements ProgressMonitor { |
| private long delayStartTime; |
| |
| private TimeUnit delayStartUnit = TimeUnit.MILLISECONDS; |
| |
| private Task task; |
| |
| /** |
| * Set an optional delay before the first output. |
| * |
| * @param time |
| * how long to wait before output. If 0 output begins on the |
| * first {@link #update(int)} call. |
| * @param unit |
| * time unit of {@code time}. |
| */ |
| public void setDelayStart(long time, TimeUnit unit) { |
| delayStartTime = time; |
| delayStartUnit = unit; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void start(int totalTasks) { |
| // Ignore the number of tasks. |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void beginTask(String title, int work) { |
| endTask(); |
| task = new Task(title, work); |
| if (delayStartTime != 0) |
| task.delay(delayStartTime, delayStartUnit); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void update(int completed) { |
| if (task != null) |
| task.update(this, completed); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void endTask() { |
| if (task != null) { |
| task.end(this); |
| task = null; |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public boolean isCancelled() { |
| return false; |
| } |
| |
| /** |
| * Update the progress monitor if the total work isn't known, |
| * |
| * @param taskName |
| * name of the task. |
| * @param workCurr |
| * number of units already completed. |
| */ |
| protected abstract void onUpdate(String taskName, int workCurr); |
| |
| /** |
| * Finish the progress monitor when the total wasn't known in advance. |
| * |
| * @param taskName |
| * name of the task. |
| * @param workCurr |
| * total number of units processed. |
| */ |
| protected abstract void onEndTask(String taskName, int workCurr); |
| |
| /** |
| * Update the progress monitor when the total is known in advance. |
| * |
| * @param taskName |
| * name of the task. |
| * @param workCurr |
| * number of units already completed. |
| * @param workTotal |
| * estimated number of units to process. |
| * @param percentDone |
| * {@code workCurr * 100 / workTotal}. |
| */ |
| protected abstract void onUpdate(String taskName, int workCurr, |
| int workTotal, int percentDone); |
| |
| /** |
| * Finish the progress monitor when the total is known in advance. |
| * |
| * @param taskName |
| * name of the task. |
| * @param workCurr |
| * total number of units processed. |
| * @param workTotal |
| * estimated number of units to process. |
| * @param percentDone |
| * {@code workCurr * 100 / workTotal}. |
| */ |
| protected abstract void onEndTask(String taskName, int workCurr, |
| int workTotal, int percentDone); |
| |
| private static class Task implements Runnable { |
| /** Title of the current task. */ |
| private final String taskName; |
| |
| /** Number of work units, or {@link ProgressMonitor#UNKNOWN}. */ |
| private final int totalWork; |
| |
| /** True when timer expires and output should occur on next update. */ |
| private volatile boolean display; |
| |
| /** Scheduled timer, supporting cancellation if task ends early. */ |
| private Future<?> timerFuture; |
| |
| /** True if the task has displayed anything. */ |
| private boolean output; |
| |
| /** Number of work units already completed. */ |
| private int lastWork; |
| |
| /** Percentage of {@link #totalWork} that is done. */ |
| private int lastPercent; |
| |
| Task(String taskName, int totalWork) { |
| this.taskName = taskName; |
| this.totalWork = totalWork; |
| this.display = true; |
| } |
| |
| void delay(long time, TimeUnit unit) { |
| display = false; |
| timerFuture = WorkQueue.getExecutor().schedule(this, time, unit); |
| } |
| |
| @Override |
| public void run() { |
| display = true; |
| } |
| |
| void update(BatchingProgressMonitor pm, int completed) { |
| lastWork += completed; |
| |
| if (totalWork == UNKNOWN) { |
| // Only display once per second, as the alarm fires. |
| if (display) { |
| pm.onUpdate(taskName, lastWork); |
| output = true; |
| restartTimer(); |
| } |
| } else { |
| // Display once per second or when 1% is done. |
| int currPercent = lastWork * 100 / totalWork; |
| if (display) { |
| pm.onUpdate(taskName, lastWork, totalWork, currPercent); |
| output = true; |
| restartTimer(); |
| lastPercent = currPercent; |
| } else if (currPercent != lastPercent) { |
| pm.onUpdate(taskName, lastWork, totalWork, currPercent); |
| output = true; |
| lastPercent = currPercent; |
| } |
| } |
| } |
| |
| private void restartTimer() { |
| display = false; |
| timerFuture = WorkQueue.getExecutor().schedule(this, 1, |
| TimeUnit.SECONDS); |
| } |
| |
| void end(BatchingProgressMonitor pm) { |
| if (output) { |
| if (totalWork == UNKNOWN) { |
| pm.onEndTask(taskName, lastWork); |
| } else { |
| int pDone = lastWork * 100 / totalWork; |
| pm.onEndTask(taskName, lastWork, totalWork, pDone); |
| } |
| } |
| if (timerFuture != null) |
| timerFuture.cancel(false /* no interrupt */); |
| } |
| } |
| } |