ProgressSpinner.java

  1. /*
  2.  * Copyright (C) 2015, 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.transport;

  11. import static java.nio.charset.StandardCharsets.UTF_8;

  12. import java.io.IOException;
  13. import java.io.OutputStream;
  14. import java.util.concurrent.TimeUnit;

  15. /**
  16.  * A simple spinner connected to an {@code OutputStream}.
  17.  * <p>
  18.  * This is class is not thread-safe. The update method may only be used from a
  19.  * single thread. Updates are sent only as frequently as {@link #update()} is
  20.  * invoked by the caller, and are capped at no more than 2 times per second by
  21.  * requiring at least 500 milliseconds between updates.
  22.  *
  23.  * @since 4.2
  24.  */
  25. public class ProgressSpinner {
  26.     private static final long MIN_REFRESH_MILLIS = 500;
  27.     private static final char[] STATES = new char[] { '-', '\\', '|', '/' };

  28.     private final OutputStream out;
  29.     private String msg;
  30.     private int state;
  31.     private boolean write;
  32.     private boolean shown;
  33.     private long nextUpdateMillis;

  34.     /**
  35.      * Initialize a new spinner.
  36.      *
  37.      * @param out
  38.      *            where to send output to.
  39.      */
  40.     public ProgressSpinner(OutputStream out) {
  41.         this.out = out;
  42.         this.write = true;
  43.     }

  44.     /**
  45.      * Begin a time consuming task.
  46.      *
  47.      * @param title
  48.      *            description of the task, suitable for human viewing.
  49.      * @param delay
  50.      *            delay to wait before displaying anything at all.
  51.      * @param delayUnits
  52.      *            unit for {@code delay}.
  53.      */
  54.     public void beginTask(String title, long delay, TimeUnit delayUnits) {
  55.         msg = title;
  56.         state = 0;
  57.         shown = false;

  58.         long now = System.currentTimeMillis();
  59.         if (delay > 0) {
  60.             nextUpdateMillis = now + delayUnits.toMillis(delay);
  61.         } else {
  62.             send(now);
  63.         }
  64.     }

  65.     /**
  66.      * Update the spinner if it is showing.
  67.      */
  68.     public void update() {
  69.         long now = System.currentTimeMillis();
  70.         if (now >= nextUpdateMillis) {
  71.             send(now);
  72.             state = (state + 1) % STATES.length;
  73.         }
  74.     }

  75.     private void send(long now) {
  76.         StringBuilder buf = new StringBuilder(msg.length() + 16);
  77.         buf.append('\r').append(msg).append("... ("); //$NON-NLS-1$
  78.         buf.append(STATES[state]);
  79.         buf.append(")  "); //$NON-NLS-1$
  80.         shown = true;
  81.         write(buf.toString());
  82.         nextUpdateMillis = now + MIN_REFRESH_MILLIS;
  83.     }

  84.     /**
  85.      * Denote the current task completed.
  86.      *
  87.      * @param result
  88.      *            text to print after the task's title
  89.      *            {@code "$title ... $result"}.
  90.      */
  91.     public void endTask(String result) {
  92.         if (shown) {
  93.             write('\r' + msg + "... " + result + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  94.         }
  95.     }

  96.     private void write(String s) {
  97.         if (write) {
  98.             try {
  99.                 out.write(s.getBytes(UTF_8));
  100.                 out.flush();
  101.             } catch (IOException e) {
  102.                 write = false;
  103.             }
  104.         }
  105.     }
  106. }