1   package com.trendmicro.grid.acl.client.util;
2   
3   import static java.lang.System.out;
4   import java.text.NumberFormat;
5   
6   /**
7    * Is a simple progress indicator for console apps.
8    *
9    * @author juergen_kellerer, 2010-07-13
10   * @version 1.0
11   */
12  public class ProgressIndicator implements Runnable {
13  
14  	static final String ANIMATION_CHARS = "/-\\|";
15  	static final char CARRIAGE_RETURN = '\r';
16  	static final long FRAME_DELAY = 150;
17  
18  	int animationIndex;
19  
20  	volatile String prefix, message = "";
21  
22  	volatile boolean stopped;
23  	volatile double progress;
24  	NumberFormat progressFormatter = NumberFormat.getInstance();
25  
26  	StringBuilder buffer;
27  
28  	public ProgressIndicator(String prefix) {
29  		this.prefix = prefix;
30  
31  		progressFormatter.setGroupingUsed(false);
32  		progressFormatter.setMinimumFractionDigits(2);
33  	}
34  
35  	@Override
36  	public void run() {
37  		try {
38  			while (!Thread.currentThread().isInterrupted() && !isClosed()) {
39  				println();
40  
41  				try {
42  					synchronized (this) {
43  						wait(FRAME_DELAY);
44  					}
45  				} catch (InterruptedException e) {
46  					Thread.currentThread().interrupt();
47  					progress = -1;
48  					break;
49  				}
50  			}
51  
52  			println();
53  		} finally {
54  			stopped = true;
55  			synchronized (this) {
56  				notifyAll(); //NOSONAR - Notify is not naked, it notifies threads waiting on stopped.
57  			}
58  		}
59  	}
60  
61  	private synchronized void println() {
62  		if (buffer == null) {
63  			if (!isClosed())
64  				buffer = new StringBuilder(128);
65  			else
66  				return;
67  		} else {
68  			buffer.setLength(0);
69  			buffer.append(CARRIAGE_RETURN);
70  		}
71  
72  		buffer.append(prefix);
73  
74  		if (!isClosed()) {
75  			buffer.append(' ').append(message).append(" [").append(ANIMATION_CHARS.charAt(animationIndex)).append(']');
76  			animationIndex++;
77  			if (animationIndex >= ANIMATION_CHARS.length())
78  				animationIndex = 0;
79  
80  			if (progress > 0) {
81  				buffer.append(' ');
82  				buffer.append(progressFormatter.format(Math.round(progress * 10000) / 100D));
83  				buffer.append('%');
84  			}
85  		} else {
86  			for (int len = String.valueOf(message).length() + 4 + 8; len >= 0; len--)
87  				buffer.append(' ');
88  		}
89  
90  		synchronized (out) {
91  			out.print(buffer.toString());
92  
93  			if (isClosed()) {
94  				out.println();
95  				buffer = null;
96  			}
97  		}
98  	}
99  
100 	public synchronized void println(String line) {
101 		synchronized (out) {
102 			if (buffer == null)
103 				out.println(line);
104 			else {
105 				out.print('\r');
106 				out.println(line);
107 			}
108 		}
109 	}
110 
111 	public void setMessage(String message) {
112 		this.message = message;
113 	}
114 
115 	public void setProgress(double processed, double total) {
116 		if (!isClosed())
117 			progress = total == 0 ? 0 : processed / total;
118 	}
119 
120 	public void append(String message) {
121 		prefix += message;
122 	}
123 
124 	public boolean isClosed() {
125 		return progress == -1;
126 	}
127 
128 	public synchronized void close() {
129 		progress = -1;
130 		notifyAll();
131 
132 		try {
133 			int maxWait = 20;
134 			while (!stopped && maxWait-- > 0) wait(100);
135 		} catch (InterruptedException ignored) {
136 			Thread.currentThread().interrupt();
137 		}
138 	}
139 }