View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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      // running states
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      // split state
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          // nothing to do
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 }