001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.lang.time;
018:
019: /**
020: * <p><code>StopWatch</code> provides a convenient API for timings.</p>
021: *
022: * <p>To start the watch, call {@link #start()}. At this point you can:</p>
023: * <ul>
024: * <li>{@link #split()} the watch to get the time whilst the watch continues in the
025: * background. {@link #unsplit()} will remove the effect of the split. At this point,
026: * these three options are available again.</li>
027: * <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch
028: * to continue. Any time between the suspend and resume will not be counted in
029: * the total. At this point, these three options are available again.</li>
030: * <li>{@link #stop()} the watch to complete the timing session.</li>
031: * </ul>
032: *
033: * <p>It is intended that the output methods {@link #toString()} and {@link #getTime()}
034: * should only be called after stop, split or suspend, however a suitable result will
035: * be returned at other points.</p>
036: *
037: * <p>NOTE: As from v2.1, the methods protect against inappropriate calls.
038: * Thus you cannot now call stop before start, resume before suspend or
039: * unsplit before split.</p>
040: *
041: * <p>1. split(), suspend(), or stop() cannot be invoked twice<br />
042: * 2. unsplit() may only be called if the watch has been split()<br />
043: * 3. resume() may only be called if the watch has been suspend()<br />
044: * 4. start() cannot be called twice without calling reset()</p>
045: *
046: * @author Stephen Colebourne
047: * @since 2.0
048: * @version $Id: StopWatch.java 504351 2007-02-06 22:49:50Z bayard $
049: */
050: public class StopWatch {
051:
052: // running states
053: private static final int STATE_UNSTARTED = 0;
054: private static final int STATE_RUNNING = 1;
055: private static final int STATE_STOPPED = 2;
056: private static final int STATE_SUSPENDED = 3;
057:
058: // split state
059: private static final int STATE_UNSPLIT = 10;
060: private static final int STATE_SPLIT = 11;
061:
062: /**
063: * The current running state of the StopWatch.
064: */
065: private int runningState = STATE_UNSTARTED;
066:
067: /**
068: * Whether the stopwatch has a split time recorded.
069: */
070: private int splitState = STATE_UNSPLIT;
071:
072: /**
073: * The start time.
074: */
075: private long startTime = -1;
076: /**
077: * The stop time.
078: */
079: private long stopTime = -1;
080:
081: /**
082: * <p>Constructor.</p>
083: */
084: public StopWatch() {
085: super ();
086: }
087:
088: /**
089: * <p>Start the stopwatch.</p>
090: *
091: * <p>This method starts a new timing session, clearing any previous values.</p>
092: *
093: * @throws IllegalStateException if the StopWatch is already running.
094: */
095: public void start() {
096: if (this .runningState == STATE_STOPPED) {
097: throw new IllegalStateException(
098: "Stopwatch must be reset before being restarted. ");
099: }
100: if (this .runningState != STATE_UNSTARTED) {
101: throw new IllegalStateException(
102: "Stopwatch already started. ");
103: }
104: stopTime = -1;
105: startTime = System.currentTimeMillis();
106: this .runningState = STATE_RUNNING;
107: }
108:
109: /**
110: * <p>Stop the stopwatch.</p>
111: *
112: * <p>This method ends a new timing session, allowing the time to be retrieved.</p>
113: *
114: * @throws IllegalStateException if the StopWatch is not running.
115: */
116: public void stop() {
117: if (this .runningState != STATE_RUNNING
118: && this .runningState != STATE_SUSPENDED) {
119: throw new IllegalStateException(
120: "Stopwatch is not running. ");
121: }
122: if (this .runningState == STATE_RUNNING) {
123: stopTime = System.currentTimeMillis();
124: }
125: this .runningState = STATE_STOPPED;
126: }
127:
128: /**
129: * <p>Resets the stopwatch. Stops it if need be. </p>
130: *
131: * <p>This method clears the internal values to allow the object to be reused.</p>
132: */
133: public void reset() {
134: this .runningState = STATE_UNSTARTED;
135: this .splitState = STATE_UNSPLIT;
136: startTime = -1;
137: stopTime = -1;
138: }
139:
140: /**
141: * <p>Split the time.</p>
142: *
143: * <p>This method sets the stop time of the watch to allow a time to be extracted.
144: * The start time is unaffected, enabling {@link #unsplit()} to continue the
145: * timing from the original start point.</p>
146: *
147: * @throws IllegalStateException if the StopWatch is not running.
148: */
149: public void split() {
150: if (this .runningState != STATE_RUNNING) {
151: throw new IllegalStateException(
152: "Stopwatch is not running. ");
153: }
154: stopTime = System.currentTimeMillis();
155: this .splitState = STATE_SPLIT;
156: }
157:
158: /**
159: * <p>Remove a split.</p>
160: *
161: * <p>This method clears the stop time. The start time is unaffected, enabling
162: * timing from the original start point to continue.</p>
163: *
164: * @throws IllegalStateException if the StopWatch has not been split.
165: */
166: public void unsplit() {
167: if (this .splitState != STATE_SPLIT) {
168: throw new IllegalStateException(
169: "Stopwatch has not been split. ");
170: }
171: stopTime = -1;
172: this .splitState = STATE_UNSPLIT;
173: }
174:
175: /**
176: * <p>Suspend the stopwatch for later resumption.</p>
177: *
178: * <p>This method suspends the watch until it is resumed. The watch will not include
179: * time between the suspend and resume calls in the total time.</p>
180: *
181: * @throws IllegalStateException if the StopWatch is not currently running.
182: */
183: public void suspend() {
184: if (this .runningState != STATE_RUNNING) {
185: throw new IllegalStateException(
186: "Stopwatch must be running to suspend. ");
187: }
188: stopTime = System.currentTimeMillis();
189: this .runningState = STATE_SUSPENDED;
190: }
191:
192: /**
193: * <p>Resume the stopwatch after a suspend.</p>
194: *
195: * <p>This method resumes the watch after it was suspended. The watch will not include
196: * time between the suspend and resume calls in the total time.</p>
197: *
198: * @throws IllegalStateException if the StopWatch has not been suspended.
199: */
200: public void resume() {
201: if (this .runningState != STATE_SUSPENDED) {
202: throw new IllegalStateException(
203: "Stopwatch must be suspended to resume. ");
204: }
205: startTime += (System.currentTimeMillis() - stopTime);
206: stopTime = -1;
207: this .runningState = STATE_RUNNING;
208: }
209:
210: /**
211: * <p>Get the time on the stopwatch.</p>
212: *
213: * <p>This is either the time between the start and the moment this method
214: * is called, or the amount of time between start and stop.</p>
215: *
216: * @return the time in milliseconds
217: */
218: public long getTime() {
219: if (this .runningState == STATE_STOPPED
220: || this .runningState == STATE_SUSPENDED) {
221: return this .stopTime - this .startTime;
222: } else if (this .runningState == STATE_UNSTARTED) {
223: return 0;
224: } else if (this .runningState == STATE_RUNNING) {
225: return System.currentTimeMillis() - this .startTime;
226: }
227: throw new RuntimeException(
228: "Illegal running state has occured. ");
229: }
230:
231: /**
232: * <p>Get the split time on the stopwatch.</p>
233: *
234: * <p>This is the time between start and latest split. </p>
235: *
236: * @return the split time in milliseconds
237: *
238: * @throws IllegalStateException if the StopWatch has not yet been split.
239: * @since 2.1
240: */
241: public long getSplitTime() {
242: if (this .splitState != STATE_SPLIT) {
243: throw new IllegalStateException(
244: "Stopwatch must be split to get the split time. ");
245: }
246: return this .stopTime - this .startTime;
247: }
248:
249: /**
250: * <p>Gets a summary of the time that the stopwatch recorded as a string.</p>
251: *
252: * <p>The format used is ISO8601-like,
253: * <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.</p>
254: *
255: * @return the time as a String
256: */
257: public String toString() {
258: return DurationFormatUtils.formatDurationHMS(getTime());
259: }
260:
261: /**
262: * <p>Gets a summary of the split time that the stopwatch recorded as a string.</p>
263: *
264: * <p>The format used is ISO8601-like,
265: * <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.</p>
266: *
267: * @return the split time as a String
268: * @since 2.1
269: */
270: public String toSplitString() {
271: return DurationFormatUtils.formatDurationHMS(getSplitTime());
272: }
273:
274: }
|