View Javadoc
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  
11  package org.eclipse.jgit.transport;
12  
13  import static java.nio.charset.StandardCharsets.UTF_8;
14  
15  import java.io.IOException;
16  import java.io.OutputStream;
17  import java.util.concurrent.TimeUnit;
18  
19  /**
20   * A simple spinner connected to an {@code OutputStream}.
21   * <p>
22   * This is class is not thread-safe. The update method may only be used from a
23   * single thread. Updates are sent only as frequently as {@link #update()} is
24   * invoked by the caller, and are capped at no more than 2 times per second by
25   * requiring at least 500 milliseconds between updates.
26   *
27   * @since 4.2
28   */
29  public class ProgressSpinner {
30  	private static final long MIN_REFRESH_MILLIS = 500;
31  	private static final char[] STATES = new char[] { '-', '\\', '|', '/' };
32  
33  	private final OutputStream out;
34  	private String msg;
35  	private int state;
36  	private boolean write;
37  	private boolean shown;
38  	private long nextUpdateMillis;
39  
40  	/**
41  	 * Initialize a new spinner.
42  	 *
43  	 * @param out
44  	 *            where to send output to.
45  	 */
46  	public ProgressSpinner(OutputStream out) {
47  		this.out = out;
48  		this.write = true;
49  	}
50  
51  	/**
52  	 * Begin a time consuming task.
53  	 *
54  	 * @param title
55  	 *            description of the task, suitable for human viewing.
56  	 * @param delay
57  	 *            delay to wait before displaying anything at all.
58  	 * @param delayUnits
59  	 *            unit for {@code delay}.
60  	 */
61  	public void beginTask(String title, long delay, TimeUnit delayUnits) {
62  		msg = title;
63  		state = 0;
64  		shown = false;
65  
66  		long now = System.currentTimeMillis();
67  		if (delay > 0) {
68  			nextUpdateMillis = now + delayUnits.toMillis(delay);
69  		} else {
70  			send(now);
71  		}
72  	}
73  
74  	/**
75  	 * Update the spinner if it is showing.
76  	 */
77  	public void update() {
78  		long now = System.currentTimeMillis();
79  		if (now >= nextUpdateMillis) {
80  			send(now);
81  			state = (state + 1) % STATES.length;
82  		}
83  	}
84  
85  	private void send(long now) {
86  		StringBuilder buf = new StringBuilder(msg.length() + 16);
87  		buf.append('\r').append(msg).append("... ("); //$NON-NLS-1$
88  		buf.append(STATES[state]);
89  		buf.append(")  "); //$NON-NLS-1$
90  		shown = true;
91  		write(buf.toString());
92  		nextUpdateMillis = now + MIN_REFRESH_MILLIS;
93  	}
94  
95  	/**
96  	 * Denote the current task completed.
97  	 *
98  	 * @param result
99  	 *            text to print after the task's title
100 	 *            {@code "$title ... $result"}.
101 	 */
102 	public void endTask(String result) {
103 		if (shown) {
104 			write('\r' + msg + "... " + result + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
105 		}
106 	}
107 
108 	private void write(String s) {
109 		if (write) {
110 			try {
111 				out.write(s.getBytes(UTF_8));
112 				out.flush();
113 			} catch (IOException e) {
114 				write = false;
115 			}
116 		}
117 	}
118 }