/* * 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 = Math.round(lastWork * 100F / 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 currPercent = Math.round(lastWork * 100F / totalWork); pm.onEndTask(taskName, lastWork, totalWork, currPercent); } } if (timerFuture != null) timerFuture.cancel(false /* no interrupt */); } } }