importjava.util.concurrent.TimeUnit;/***
* StopWatch
provides a convenient API for timings.
*
*
*
* To start the watch, call {@link#start()} or {@linkStopWatch#createStarted()}. At this point you can:
*
*
*
{@link#split()} the watch to get the time whilst the watch continues in the background. {@link#unsplit()} will* remove the effect of the split. At this point, these three options are available again.
*
{@link#suspend()} the watch to pause it. {@link#resume()} allows the watch to continue. Any time between the* suspend and resume will not be counted in the total. At this point, these three options are available again.
*
{@link#stop()} the watch to complete the timing session.*
*
*
* It is intended that the output methods {@link#toString()} and {@link#getTime()} should only be called after stop,
* split or suspend, however a suitable result will be returned at other points.
*
*
*
* NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you cannot now call stop before start,
* resume before suspend or unsplit before split.
*
*
*
* 1. split(), suspend(), or stop() cannot be invoked twice
* 2. unsplit() may only be called if the watch has been split()
* 3. resume() may only be called if the watch has been suspend()
* 4. start() cannot be called twice without calling reset()
*
*
*
This class is not thread-safe
*
*@since2.0*/
public classStopWatch {private static final long NANO_2_MILLIS = 1000000L;/*** Provides a started stopwatch for convenience.
*
*@returnStopWatch a stopwatch that's already been started.
*
*@since3.5*/
public staticStopWatch createStarted() {final StopWatch sw = newStopWatch();
sw.start();returnsw;
}/*** Enumeration type which indicates the status of stopwatch.*/
private enumState {
UNSTARTED {
@OverridebooleanisStarted() {return false;
}
@OverridebooleanisStopped() {return true;
}
@OverridebooleanisSuspended() {return false;
}
},
RUNNING {
@OverridebooleanisStarted() {return true;
}
@OverridebooleanisStopped() {return false;
}
@OverridebooleanisSuspended() {return false;
}
},
STOPPED {
@OverridebooleanisStarted() {return false;
}
@OverridebooleanisStopped() {return true;
}
@OverridebooleanisSuspended() {return false;
}
},
SUSPENDED {
@OverridebooleanisStarted() {return true;
}
@OverridebooleanisStopped() {return false;
}
@OverridebooleanisSuspended() {return true;
}
};/***
* The method is used to find out if the StopWatch is started. A suspended
* StopWatch is also started watch.
*
*@returnboolean
* If the StopWatch is started.*/
abstract booleanisStarted();/***
* This method is used to find out whether the StopWatch is stopped. The
* stopwatch which's not yet started and explicitly stopped stopwatch is
* considered as stopped.
*
*
*@returnboolean
* If the StopWatch is stopped.*/
abstract booleanisStopped();/***
* This method is used to find out whether the StopWatch is suspended.
*
*
*@returnboolean
* If the StopWatch is suspended.*/
abstract booleanisSuspended();
}/*** Enumeration type which indicates the split status of stopwatch.*/
private enumSplitState {
SPLIT,
UNSPLIT
}/*** The current running state of the StopWatch.*/
private State runningState =State.UNSTARTED;/*** Whether the stopwatch has a split time recorded.*/
private SplitState splitState =SplitState.UNSPLIT;/*** The start time.*/
private longstartTime;/*** The start time in Millis - nanoTime is only for elapsed time so we
* need to also store the currentTimeMillis to maintain the old
* getStartTime API.*/
private longstartTimeMillis;/*** The stop time.*/
private longstopTime;/***
* Constructor.
*
*/publicStopWatch() {super();
}/***
* Start the stopwatch.
*
*
*
* This method starts a new timing session, clearing any previous values.
*
*
*@throwsIllegalStateException
* if the StopWatch is already running.*/
public voidstart() {if (this.runningState ==State.STOPPED) {throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
}if (this.runningState !=State.UNSTARTED) {throw new IllegalStateException("Stopwatch already started. ");
}this.startTime =System.nanoTime();this.startTimeMillis =System.currentTimeMillis();this.runningState =State.RUNNING;
}/***
* Stop the stopwatch.
*
*
*
* This method ends a new timing session, allowing the time to be retrieved.
*
*
*@throwsIllegalStateException
* if the StopWatch is not running.*/
public voidstop() {if (this.runningState != State.RUNNING && this.runningState !=State.SUSPENDED) {throw new IllegalStateException("Stopwatch is not running. ");
}if (this.runningState ==State.RUNNING) {this.stopTime =System.nanoTime();
}this.runningState =State.STOPPED;
}/***
* Resets the stopwatch. Stops it if need be.
*
*
*
* This method clears the internal values to allow the object to be reused.
*
*/public voidreset() {this.runningState =State.UNSTARTED;this.splitState =SplitState.UNSPLIT;
}/***
* Split the time.
*
*
*
* This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected,
* enabling {@link#unsplit()} to continue the timing from the original start point.
*
*
*@throwsIllegalStateException
* if the StopWatch is not running.*/
public voidsplit() {if (this.runningState !=State.RUNNING) {throw new IllegalStateException("Stopwatch is not running. ");
}this.stopTime =System.nanoTime();this.splitState =SplitState.SPLIT;
}/***
* Remove a split.
*
*
*
* This method clears the stop time. The start time is unaffected, enabling timing from the original start point to
* continue.
*
*
*@throwsIllegalStateException
* if the StopWatch has not been split.*/
public voidunsplit() {if (this.splitState !=SplitState.SPLIT) {throw new IllegalStateException("Stopwatch has not been split. ");
}this.splitState =SplitState.UNSPLIT;
}/***
* Suspend the stopwatch for later resumption.
*
*
*
* This method suspends the watch until it is resumed. The watch will not include time between the suspend and
* resume calls in the total time.
*
*
*@throwsIllegalStateException
* if the StopWatch is not currently running.*/
public voidsuspend() {if (this.runningState !=State.RUNNING) {throw new IllegalStateException("Stopwatch must be running to suspend. ");
}this.stopTime =System.nanoTime();this.runningState =State.SUSPENDED;
}/***
* Resume the stopwatch after a suspend.
*
*
*
* This method resumes the watch after it was suspended. The watch will not include time between the suspend and
* resume calls in the total time.
*
*
*@throwsIllegalStateException
* if the StopWatch has not been suspended.*/
public voidresume() {if (this.runningState !=State.SUSPENDED) {throw new IllegalStateException("Stopwatch must be suspended to resume. ");
}this.startTime += System.nanoTime() - this.stopTime;this.runningState =State.RUNNING;
}/***
* Get the time on the stopwatch.
*
*
*
* This is either the time between the start and the moment this method is called, or the amount of time between
* start and stop.
*
*
*@returnthe time in milliseconds*/
public longgetTime() {return getNanoTime() /NANO_2_MILLIS;
}/***
* Get the time on the stopwatch in the specified TimeUnit.
*
*
*
* This is either the time between the start and the moment this method is called, or the amount of time between
* start and stop. The resulting time will be expressed in the desired TimeUnit with any remainder rounded down.
* For example, if the specified unit is {@codeTimeUnit.HOURS} and the stopwatch time is 59 minutes, then the
* result returned will be {@code0}.
*
*
*@paramtimeUnit the unit of time, not null
*@returnthe time in the specified TimeUnit, rounded down
*@since3.5*/
public long getTime(finalTimeUnit timeUnit) {returntimeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS);
}/***
* Get the time on the stopwatch in nanoseconds.
*
*
*
* This is either the time between the start and the moment this method is called, or the amount of time between
* start and stop.
*
*
*@returnthe time in nanoseconds
*@since3.0*/
public longgetNanoTime() {if (this.runningState == State.STOPPED || this.runningState ==State.SUSPENDED) {return this.stopTime - this.startTime;
}else if (this.runningState ==State.UNSTARTED) {return 0;
}else if (this.runningState ==State.RUNNING) {return System.nanoTime() - this.startTime;
}throw new RuntimeException("Illegal running state has occurred.");
}/***
* Get the split time on the stopwatch.
*
*
*
* This is the time between start and latest split.
*
*
*@returnthe split time in milliseconds
*
*@throwsIllegalStateException
* if the StopWatch has not yet been split.
*@since2.1*/
public longgetSplitTime() {return getSplitNanoTime() /NANO_2_MILLIS;
}/***
* Get the split time on the stopwatch in nanoseconds.
*
*
*
* This is the time between start and latest split.
*
*
*@returnthe split time in nanoseconds
*
*@throwsIllegalStateException
* if the StopWatch has not yet been split.
*@since3.0*/
public longgetSplitNanoTime() {if (this.splitState !=SplitState.SPLIT) {throw new IllegalStateException("Stopwatch must be split to get the split time. ");
}return this.stopTime - this.startTime;
}/*** Returns the time this stopwatch was started.
*
*@returnthe time this stopwatch was started
*@throwsIllegalStateException
* if this StopWatch has not been started
*@since2.4*/
public longgetStartTime() {if (this.runningState ==State.UNSTARTED) {throw new IllegalStateException("Stopwatch has not been started");
}//System.nanoTime is for elapsed time
return this.startTimeMillis;
}/***
* Gets a summary of the time that the stopwatch recorded as a string.
*
*
*
* The format used is ISO 8601-like, hours:minutes:seconds.milliseconds.
*
*
*@returnthe time as a String*/@OverridepublicString toString() {returnDurationFormatUtils.formatDurationHMS(getTime());
}/***
* Gets a summary of the split time that the stopwatch recorded as a string.
*
*
*
* The format used is ISO 8601-like, hours:minutes:seconds.milliseconds.
*
*
*@returnthe split time as a String
*@since2.1*/
publicString toSplitString() {returnDurationFormatUtils.formatDurationHMS(getSplitTime());
}/***
* The method is used to find out if the StopWatch is started. A suspended
* StopWatch is also started watch.
*
*
*@returnboolean
* If the StopWatch is started.
*@since3.2*/
public booleanisStarted() {returnrunningState.isStarted();
}/***
* This method is used to find out whether the StopWatch is suspended.
*
*
*@returnboolean
* If the StopWatch is suspended.
*@since3.2*/
public booleanisSuspended() {returnrunningState.isSuspended();
}/***
* This method is used to find out whether the StopWatch is stopped. The
* stopwatch which's not yet started and explicitly stopped stopwatch is
* considered as stopped.
*
*
*@returnboolean
* If the StopWatch is stopped.
*@since3.2*/
public booleanisStopped() {returnrunningState.isStopped();
}
}