BatchingProgressMonitor.java

  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. package org.eclipse.jgit.lib;

  11. import java.util.concurrent.Future;
  12. import java.util.concurrent.TimeUnit;

  13. import org.eclipse.jgit.lib.internal.WorkQueue;

  14. /**
  15.  * ProgressMonitor that batches update events.
  16.  */
  17. public abstract class BatchingProgressMonitor implements ProgressMonitor {
  18.     private long delayStartTime;

  19.     private TimeUnit delayStartUnit = TimeUnit.MILLISECONDS;

  20.     private Task task;

  21.     /**
  22.      * Set an optional delay before the first output.
  23.      *
  24.      * @param time
  25.      *            how long to wait before output. If 0 output begins on the
  26.      *            first {@link #update(int)} call.
  27.      * @param unit
  28.      *            time unit of {@code time}.
  29.      */
  30.     public void setDelayStart(long time, TimeUnit unit) {
  31.         delayStartTime = time;
  32.         delayStartUnit = unit;
  33.     }

  34.     /** {@inheritDoc} */
  35.     @Override
  36.     public void start(int totalTasks) {
  37.         // Ignore the number of tasks.
  38.     }

  39.     /** {@inheritDoc} */
  40.     @Override
  41.     public void beginTask(String title, int work) {
  42.         endTask();
  43.         task = new Task(title, work);
  44.         if (delayStartTime != 0)
  45.             task.delay(delayStartTime, delayStartUnit);
  46.     }

  47.     /** {@inheritDoc} */
  48.     @Override
  49.     public void update(int completed) {
  50.         if (task != null)
  51.             task.update(this, completed);
  52.     }

  53.     /** {@inheritDoc} */
  54.     @Override
  55.     public void endTask() {
  56.         if (task != null) {
  57.             task.end(this);
  58.             task = null;
  59.         }
  60.     }

  61.     /** {@inheritDoc} */
  62.     @Override
  63.     public boolean isCancelled() {
  64.         return false;
  65.     }

  66.     /**
  67.      * Update the progress monitor if the total work isn't known,
  68.      *
  69.      * @param taskName
  70.      *            name of the task.
  71.      * @param workCurr
  72.      *            number of units already completed.
  73.      */
  74.     protected abstract void onUpdate(String taskName, int workCurr);

  75.     /**
  76.      * Finish the progress monitor when the total wasn't known in advance.
  77.      *
  78.      * @param taskName
  79.      *            name of the task.
  80.      * @param workCurr
  81.      *            total number of units processed.
  82.      */
  83.     protected abstract void onEndTask(String taskName, int workCurr);

  84.     /**
  85.      * Update the progress monitor when the total is known in advance.
  86.      *
  87.      * @param taskName
  88.      *            name of the task.
  89.      * @param workCurr
  90.      *            number of units already completed.
  91.      * @param workTotal
  92.      *            estimated number of units to process.
  93.      * @param percentDone
  94.      *            {@code workCurr * 100 / workTotal}.
  95.      */
  96.     protected abstract void onUpdate(String taskName, int workCurr,
  97.             int workTotal, int percentDone);

  98.     /**
  99.      * Finish the progress monitor when the total is known in advance.
  100.      *
  101.      * @param taskName
  102.      *            name of the task.
  103.      * @param workCurr
  104.      *            total number of units processed.
  105.      * @param workTotal
  106.      *            estimated number of units to process.
  107.      * @param percentDone
  108.      *            {@code workCurr * 100 / workTotal}.
  109.      */
  110.     protected abstract void onEndTask(String taskName, int workCurr,
  111.             int workTotal, int percentDone);

  112.     private static class Task implements Runnable {
  113.         /** Title of the current task. */
  114.         private final String taskName;

  115.         /** Number of work units, or {@link ProgressMonitor#UNKNOWN}. */
  116.         private final int totalWork;

  117.         /** True when timer expires and output should occur on next update. */
  118.         private volatile boolean display;

  119.         /** Scheduled timer, supporting cancellation if task ends early. */
  120.         private Future<?> timerFuture;

  121.         /** True if the task has displayed anything. */
  122.         private boolean output;

  123.         /** Number of work units already completed. */
  124.         private int lastWork;

  125.         /** Percentage of {@link #totalWork} that is done. */
  126.         private int lastPercent;

  127.         Task(String taskName, int totalWork) {
  128.             this.taskName = taskName;
  129.             this.totalWork = totalWork;
  130.             this.display = true;
  131.         }

  132.         void delay(long time, TimeUnit unit) {
  133.             display = false;
  134.             timerFuture = WorkQueue.getExecutor().schedule(this, time, unit);
  135.         }

  136.         @Override
  137.         public void run() {
  138.             display = true;
  139.         }

  140.         void update(BatchingProgressMonitor pm, int completed) {
  141.             lastWork += completed;

  142.             if (totalWork == UNKNOWN) {
  143.                 // Only display once per second, as the alarm fires.
  144.                 if (display) {
  145.                     pm.onUpdate(taskName, lastWork);
  146.                     output = true;
  147.                     restartTimer();
  148.                 }
  149.             } else {
  150.                 // Display once per second or when 1% is done.
  151.                 int currPercent = lastWork * 100 / totalWork;
  152.                 if (display) {
  153.                     pm.onUpdate(taskName, lastWork, totalWork, currPercent);
  154.                     output = true;
  155.                     restartTimer();
  156.                     lastPercent = currPercent;
  157.                 } else if (currPercent != lastPercent) {
  158.                     pm.onUpdate(taskName, lastWork, totalWork, currPercent);
  159.                     output = true;
  160.                     lastPercent = currPercent;
  161.                 }
  162.             }
  163.         }

  164.         private void restartTimer() {
  165.             display = false;
  166.             timerFuture = WorkQueue.getExecutor().schedule(this, 1,
  167.                     TimeUnit.SECONDS);
  168.         }

  169.         void end(BatchingProgressMonitor pm) {
  170.             if (output) {
  171.                 if (totalWork == UNKNOWN) {
  172.                     pm.onEndTask(taskName, lastWork);
  173.                 } else {
  174.                     int pDone = lastWork * 100 / totalWork;
  175.                     pm.onEndTask(taskName, lastWork, totalWork, pDone);
  176.                 }
  177.             }
  178.             if (timerFuture != null)
  179.                 timerFuture.cancel(false /* no interrupt */);
  180.         }
  181.     }
  182. }