| /* |
| * 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.time.Duration; |
| import java.time.Instant; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.eclipse.jgit.lib.internal.WorkQueue; |
| import org.eclipse.jgit.util.SystemReader; |
| |
| /** |
| * ProgressMonitor that batches update events. |
| */ |
| public abstract class BatchingProgressMonitor implements ProgressMonitor { |
| private static boolean performanceTrace = SystemReader.getInstance() |
| .isPerformanceTraceEnabled(); |
| |
| private long delayStartTime; |
| |
| private TimeUnit delayStartUnit = TimeUnit.MILLISECONDS; |
| |
| private Task task; |
| |
| private Boolean showDuration; |
| |
| /** |
| * 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; |
| } |
| |
| @Override |
| public void start(int totalTasks) { |
| // Ignore the number of tasks. |
| } |
| |
| @Override |
| public void beginTask(String title, int work) { |
| endTask(); |
| task = new Task(title, work); |
| if (delayStartTime != 0) |
| task.delay(delayStartTime, delayStartUnit); |
| } |
| |
| @Override |
| public void update(int completed) { |
| if (task != null) |
| task.update(this, completed); |
| } |
| |
| @Override |
| public void endTask() { |
| if (task != null) { |
| task.end(this); |
| task = null; |
| } |
| } |
| |
| @Override |
| public boolean isCancelled() { |
| return false; |
| } |
| |
| @Override |
| public void showDuration(boolean enabled) { |
| showDuration = Boolean.valueOf(enabled); |
| } |
| |
| /** |
| * Update the progress monitor if the total work isn't known, |
| * |
| * @param taskName |
| * name of the task. |
| * @param workCurr |
| * number of units already completed. |
| * @param duration |
| * how long this task runs |
| * @since 6.5 |
| */ |
| protected abstract void onUpdate(String taskName, int workCurr, |
| Duration duration); |
| |
| /** |
| * 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. |
| * @param duration |
| * how long this task runs |
| * @since 6.5 |
| */ |
| protected abstract void onEndTask(String taskName, int workCurr, |
| Duration duration); |
| |
| /** |
| * 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}. |
| * @param duration |
| * how long this task runs |
| * @since 6.5 |
| */ |
| protected abstract void onUpdate(String taskName, int workCurr, |
| int workTotal, int percentDone, Duration duration); |
| |
| /** |
| * 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}. |
| * @param duration |
| * duration of the task |
| * @since 6.5 |
| */ |
| protected abstract void onEndTask(String taskName, int workCurr, |
| int workTotal, int percentDone, Duration duration); |
| |
| private boolean showDuration() { |
| return showDuration != null ? showDuration.booleanValue() |
| : performanceTrace; |
| } |
| |
| /** |
| * Append formatted duration if system property or environment variable |
| * GIT_TRACE_PERFORMANCE is set to "true". If both are defined the system |
| * property takes precedence. |
| * |
| * @param s |
| * StringBuilder to append the formatted duration to |
| * @param duration |
| * duration to format |
| * @since 6.5 |
| */ |
| @SuppressWarnings({ "boxing", "nls" }) |
| protected void appendDuration(StringBuilder s, Duration duration) { |
| if (!showDuration()) { |
| return; |
| } |
| long hours = duration.toHours(); |
| int minutes = duration.toMinutesPart(); |
| int seconds = duration.toSecondsPart(); |
| s.append(" ["); |
| if (hours > 0) { |
| s.append(hours).append(':'); |
| s.append(String.format("%02d", minutes)).append(':'); |
| s.append(String.format("%02d", seconds)); |
| } else if (minutes > 0) { |
| s.append(minutes).append(':'); |
| s.append(String.format("%02d", seconds)); |
| } else { |
| s.append(seconds); |
| } |
| s.append('.').append(String.format("%03d", duration.toMillisPart())); |
| if (hours > 0) { |
| s.append('h'); |
| } else if (minutes > 0) { |
| s.append('m'); |
| } else { |
| s.append('s'); |
| } |
| s.append(']'); |
| } |
| |
| 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; |
| |
| private final Instant startTime; |
| |
| Task(String taskName, int totalWork) { |
| this.taskName = taskName; |
| this.totalWork = totalWork; |
| this.display = true; |
| this.startTime = Instant.now(); |
| } |
| |
| 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, elapsedTime()); |
| 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, |
| elapsedTime()); |
| output = true; |
| restartTimer(); |
| lastPercent = currPercent; |
| } else if (currPercent != lastPercent) { |
| pm.onUpdate(taskName, lastWork, totalWork, currPercent, |
| elapsedTime()); |
| 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, elapsedTime()); |
| } else { |
| int currPercent = Math.round(lastWork * 100F / totalWork); |
| pm.onEndTask(taskName, lastWork, totalWork, currPercent, elapsedTime()); |
| } |
| } |
| if (timerFuture != null) |
| timerFuture.cancel(false /* no interrupt */); |
| } |
| |
| private Duration elapsedTime() { |
| return Duration.between(startTime, Instant.now()); |
| } |
| } |
| } |