1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.fulcrum.yaafi.interceptor.util;
20
21 /**
22 * <p><code>StopWatch</code> provides a convenient API for timings.</p>
23 *
24 * <p>To start the watch, call {@link #start()}. At this point you can:</p>
25 * <ul>
26 * <li>{@link #split()} the watch to get the time whilst the watch continues in the
27 * background. {@link #unsplit()} will remove the effect of the split. At this point,
28 * these three options are available again.</li>
29 * <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch
30 * to continue. Any time between the suspend and resume will not be counted in
31 * the total. At this point, these three options are available again.</li>
32 * <li>{@link #stop()} the watch to complete the timing session.</li>
33 * </ul>
34 *
35 * <p>It is intended that the output methods {@link #toString()} and {@link #getTime()}
36 * should only be called after stop, split or suspend, however a suitable result will
37 * be returned at other points.</p>
38 *
39 * <p>NOTE: As from v2.1, the methods protect against inappropriate calls.
40 * Thus you cannot now call stop before start, resume before suspend or
41 * unsplit before split.</p>
42 *
43 * <p>1. split(), suspend(), or stop() cannot be invoked twice<br />
44 * 2. unsplit() may only be called if the watch has been split()<br />
45 * 3. resume() may only be called if the watch has been suspend()<br />
46 * 4. start() cannot be called twice without calling reset()</p>
47 *
48 * @author Henri Yandell
49 * @author Stephen Colebourne
50 * @since 2.0
51 * @version $Id: StopWatch.java,v 1.1 2005/09/29 13:18:37 sigi Exp $
52 */
53 public class StopWatch {
54
55
56 private static final int STATE_UNSTARTED = 0;
57 private static final int STATE_RUNNING = 1;
58 private static final int STATE_STOPPED = 2;
59 private static final int STATE_SUSPENDED = 3;
60
61
62 private static final int STATE_UNSPLIT = 10;
63 private static final int STATE_SPLIT = 11;
64
65 /**
66 * The current running state of the StopWatch.
67 */
68 private int runningState = STATE_UNSTARTED;
69
70 /**
71 * Whether the stopwatch has a split time recorded.
72 */
73 private int splitState = STATE_UNSPLIT;
74
75 /**
76 * The start time.
77 */
78 private long startTime = -1;
79 /**
80 * The stop time.
81 */
82 private long stopTime = -1;
83
84 /**
85 * <p>Constructor.</p>
86 */
87 public StopWatch() {
88
89 }
90
91 /**
92 * <p>Start the stopwatch.</p>
93 *
94 * <p>This method starts a new timing session, clearing any previous values.</p>
95 *
96 * @throws IllegalStateException if the StopWatch is already running.
97 */
98 public void start()
99 {
100 if (this.runningState == STATE_STOPPED)
101 {
102 throw new IllegalStateException(
103 "Stopwatch must be reset before being restarted. " );
104 }
105 if (this.runningState != STATE_UNSTARTED)
106 {
107 throw new IllegalStateException( "Stopwatch already started. " );
108 }
109 stopTime = -1;
110 startTime = System.currentTimeMillis();
111 this.runningState = STATE_RUNNING;
112 }
113
114 /**
115 * <p>Stop the stopwatch.</p>
116 *
117 * <p>This method ends a new timing session, allowing the time to be retrieved.</p>
118 *
119 * @throws IllegalStateException if the StopWatch is not running.
120 */
121 public void stop()
122 {
123 if (this.runningState != STATE_RUNNING
124 && this.runningState != STATE_SUSPENDED)
125 {
126 throw new IllegalStateException( "Stopwatch is not running. " );
127 }
128 stopTime = System.currentTimeMillis();
129 this.runningState = STATE_STOPPED;
130 }
131
132 /**
133 * <p>Resets the stopwatch. Stops it if need be. </p>
134 *
135 * <p>This method clears the internal values to allow the object to be reused.</p>
136 */
137 public void reset()
138 {
139 this.runningState = STATE_UNSTARTED;
140 this.splitState = STATE_UNSPLIT;
141 startTime = -1;
142 stopTime = -1;
143 }
144
145 /**
146 * <p>Split the time.</p>
147 *
148 * <p>This method sets the stop time of the watch to allow a time to be extracted.
149 * The start time is unaffected, enabling {@link #unsplit()} to continue the
150 * timing from the original start point.</p>
151 *
152 * @throws IllegalStateException if the StopWatch is not running.
153 */
154 public void split()
155 {
156 if (this.runningState != STATE_RUNNING)
157 {
158 throw new IllegalStateException( "Stopwatch is not running. " );
159 }
160 stopTime = System.currentTimeMillis();
161 this.splitState = STATE_SPLIT;
162 }
163
164 /**
165 * <p>Remove a split.</p>
166 *
167 * <p>This method clears the stop time. The start time is unaffected, enabling
168 * timing from the original start point to continue.</p>
169 *
170 * @throws IllegalStateException if the StopWatch has not been split.
171 */
172 public void unsplit()
173 {
174 if (this.splitState != STATE_SPLIT)
175 {
176 throw new IllegalStateException( "Stopwatch has not been split. " );
177 }
178 stopTime = -1;
179 this.splitState = STATE_UNSPLIT;
180 }
181
182 /**
183 * <p>Suspend the stopwatch for later resumption.</p>
184 *
185 * <p>This method suspends the watch until it is resumed. The watch will not include
186 * time between the suspend and resume calls in the total time.</p>
187 *
188 * @throws IllegalStateException if the StopWatch is not currently running.
189 */
190 public void suspend()
191 {
192 if (this.runningState != STATE_RUNNING)
193 {
194 throw new IllegalStateException(
195 "Stopwatch must be running to suspend. " );
196 }
197 stopTime = System.currentTimeMillis();
198 this.runningState = STATE_SUSPENDED;
199 }
200
201 /**
202 * <p>Resume the stopwatch after a suspend.</p>
203 *
204 * <p>This method resumes the watch after it was suspended. The watch will not include
205 * time between the suspend and resume calls in the total time.</p>
206 *
207 * @throws IllegalStateException if the StopWatch has not been suspended.
208 */
209 public void resume()
210 {
211 if (this.runningState != STATE_SUSPENDED)
212 {
213 throw new IllegalStateException(
214 "Stopwatch must be suspended to resume. " );
215 }
216 startTime += (System.currentTimeMillis() - stopTime);
217 stopTime = -1;
218 this.runningState = STATE_RUNNING;
219 }
220
221 /**
222 * <p>Get the time on the stopwatch.</p>
223 *
224 * <p>This is either the time between the start and the moment this method
225 * is called, or the amount of time between start and stop.</p>
226 *
227 * @return the time in milliseconds
228 */
229 public long getTime()
230 {
231 if (this.runningState == STATE_STOPPED
232 || this.runningState == STATE_SUSPENDED)
233 {
234 return this.stopTime - this.startTime;
235 }
236 else if (this.runningState == STATE_UNSTARTED)
237 {
238 return 0;
239 }
240 else if (this.runningState == STATE_RUNNING)
241 {
242 return System.currentTimeMillis() - this.startTime;
243 }
244 throw new RuntimeException( "Illegal running state has occured. " );
245 }
246
247 /**
248 * <p>Get the split time on the stopwatch.</p>
249 *
250 * <p>This is the time between start and latest split. </p>
251 *
252 * @return the split time in milliseconds
253 *
254 * @throws IllegalStateException if the StopWatch has not yet been split.
255 * @since 2.1
256 */
257 public long getSplitTime()
258 {
259 if (this.splitState != STATE_SPLIT)
260 {
261 throw new IllegalStateException(
262 "Stopwatch must be split to get the split time. "
263 );
264 }
265 return this.stopTime - this.startTime;
266 }
267
268 /**
269 * <p>Gets a summary of the time that the stopwatch recorded as a string.</p>
270 *
271 * @return the time as a String
272 */
273 public String toString() {
274 return getTime()+"ms";
275 }
276 }