Coverage Report - org.apache.commons.lang3.time.StopWatch
 
Classes in this File Line Coverage Branch Coverage Complexity
StopWatch
98%
63/64
96%
29/30
1,8
StopWatch$1
N/A
N/A
1,8
StopWatch$SplitState
100%
3/3
N/A
1,8
StopWatch$State
100%
5/5
N/A
1,8
StopWatch$State$1
100%
4/4
N/A
1,8
StopWatch$State$2
100%
4/4
N/A
1,8
StopWatch$State$3
100%
4/4
N/A
1,8
StopWatch$State$4
100%
4/4
N/A
1,8
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  * 
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  * 
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.lang3.time;
 19  
 
 20  
 import java.util.concurrent.TimeUnit;
 21  
 
 22  
 /**
 23  
  * <p>
 24  
  * <code>StopWatch</code> provides a convenient API for timings.
 25  
  * </p>
 26  
  * 
 27  
  * <p>
 28  
  * To start the watch, call {@link #start()} or {@link StopWatch#createStarted()}. At this point you can:
 29  
  * </p>
 30  
  * <ul>
 31  
  * <li>{@link #split()} the watch to get the time whilst the watch continues in the background. {@link #unsplit()} will
 32  
  * remove the effect of the split. At this point, these three options are available again.</li>
 33  
  * <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch to continue. Any time between the
 34  
  * suspend and resume will not be counted in the total. At this point, these three options are available again.</li>
 35  
  * <li>{@link #stop()} the watch to complete the timing session.</li>
 36  
  * </ul>
 37  
  * 
 38  
  * <p>
 39  
  * It is intended that the output methods {@link #toString()} and {@link #getTime()} should only be called after stop,
 40  
  * split or suspend, however a suitable result will be returned at other points.
 41  
  * </p>
 42  
  * 
 43  
  * <p>
 44  
  * NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you cannot now call stop before start,
 45  
  * resume before suspend or unsplit before split.
 46  
  * </p>
 47  
  * 
 48  
  * <p>
 49  
  * 1. split(), suspend(), or stop() cannot be invoked twice<br>
 50  
  * 2. unsplit() may only be called if the watch has been split()<br>
 51  
  * 3. resume() may only be called if the watch has been suspend()<br>
 52  
  * 4. start() cannot be called twice without calling reset()
 53  
  * </p>
 54  
  * 
 55  
  * <p>This class is not thread-safe</p>
 56  
  * 
 57  
  * @since 2.0
 58  
  */
 59  
 public class StopWatch {
 60  
 
 61  
     private static final long NANO_2_MILLIS = 1000000L;
 62  
 
 63  
 
 64  
     /**
 65  
      * Provides a started stopwatch for convenience.
 66  
      *
 67  
      * @return StopWatch a stopwatch that's already been started. 
 68  
      *
 69  
      * @since 3.5
 70  
      */
 71  
     public static StopWatch createStarted() {
 72  4
         StopWatch sw = new StopWatch();
 73  4
         sw.start();
 74  4
         return sw;
 75  
     }
 76  
     
 77  
     /**
 78  
      * Enumeration type which indicates the status of stopwatch.
 79  
      */
 80  18
     private enum State {
 81  
 
 82  2
         UNSTARTED {
 83  2
             @Override boolean isStarted() { return false; }
 84  2
             @Override boolean isStopped() { return true;  }
 85  2
             @Override boolean isSuspended() { return false; }
 86  
         },
 87  2
         RUNNING {
 88  4
             @Override boolean isStarted() { return true; }
 89  2
             @Override boolean isStopped() { return false; }
 90  2
             @Override boolean isSuspended() { return false; }
 91  
         },
 92  2
         STOPPED {
 93  2
             @Override boolean isStarted() { return false; }
 94  2
             @Override boolean isStopped() { return true; }
 95  2
             @Override boolean isSuspended() { return false; }
 96  
         },
 97  2
         SUSPENDED {
 98  2
             @Override boolean isStarted() { return true; }
 99  2
             @Override boolean isStopped() { return false; }
 100  2
             @Override  boolean isSuspended() { return true; }
 101  
         };
 102  
 
 103  
         /**
 104  
          * <p>
 105  
          * The method is used to find out if the StopWatch is started. A suspended
 106  
          * StopWatch is also started watch.
 107  
          * </p>
 108  
 
 109  
          * @return boolean
 110  
          *             If the StopWatch is started.
 111  
          */
 112  
         abstract boolean isStarted();
 113  
 
 114  
         /**
 115  
          * <p>
 116  
          * This method is used to find out whether the StopWatch is stopped. The
 117  
          * stopwatch which's not yet started and explicitly stopped stopwatch is
 118  
          * considered as stopped.
 119  
          * </p>
 120  
          *
 121  
          * @return boolean
 122  
          *             If the StopWatch is stopped.
 123  
          */
 124  
         abstract boolean isStopped();
 125  
 
 126  
         /**
 127  
          * <p>
 128  
          * This method is used to find out whether the StopWatch is suspended.
 129  
          * </p>
 130  
          *
 131  
          * @return boolean
 132  
          *             If the StopWatch is suspended.
 133  
          */
 134  
         abstract boolean isSuspended();
 135  
     }
 136  
 
 137  
     /**
 138  
      * Enumeration type which indicates the split status of stopwatch.
 139  
      */    
 140  6
     private enum SplitState {
 141  2
         SPLIT,
 142  2
         UNSPLIT
 143  
     }
 144  
     /**
 145  
      * The current running state of the StopWatch.
 146  
      */
 147  20
     private State runningState = State.UNSTARTED;
 148  
 
 149  
     /**
 150  
      * Whether the stopwatch has a split time recorded.
 151  
      */
 152  20
     private SplitState splitState = SplitState.UNSPLIT;
 153  
 
 154  
     /**
 155  
      * The start time.
 156  
      */
 157  
     private long startTime;
 158  
 
 159  
     /**
 160  
      * The start time in Millis - nanoTime is only for elapsed time so we 
 161  
      * need to also store the currentTimeMillis to maintain the old 
 162  
      * getStartTime API.
 163  
      */
 164  
     private long startTimeMillis;
 165  
 
 166  
     /**
 167  
      * The stop time.
 168  
      */
 169  
     private long stopTime;
 170  
 
 171  
     /**
 172  
      * <p>
 173  
      * Constructor.
 174  
      * </p>
 175  
      */
 176  
     public StopWatch() {
 177  20
         super();
 178  20
     }
 179  
 
 180  
     /**
 181  
      * <p>
 182  
      * Start the stopwatch.
 183  
      * </p>
 184  
      * 
 185  
      * <p>
 186  
      * This method starts a new timing session, clearing any previous values.
 187  
      * </p>
 188  
      * 
 189  
      * @throws IllegalStateException
 190  
      *             if the StopWatch is already running.
 191  
      */
 192  
     public void start() {
 193  24
         if (this.runningState == State.STOPPED) {
 194  2
             throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
 195  
         }
 196  22
         if (this.runningState != State.UNSTARTED) {
 197  2
             throw new IllegalStateException("Stopwatch already started. ");
 198  
         }
 199  20
         this.startTime = System.nanoTime();
 200  20
         this.startTimeMillis = System.currentTimeMillis();
 201  20
         this.runningState = State.RUNNING;
 202  20
     }
 203  
 
 204  
 
 205  
     /**
 206  
      * <p>
 207  
      * Stop the stopwatch.
 208  
      * </p>
 209  
      * 
 210  
      * <p>
 211  
      * This method ends a new timing session, allowing the time to be retrieved.
 212  
      * </p>
 213  
      * 
 214  
      * @throws IllegalStateException
 215  
      *             if the StopWatch is not running.
 216  
      */
 217  
     public void stop() {
 218  16
         if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) {
 219  4
             throw new IllegalStateException("Stopwatch is not running. ");
 220  
         }
 221  12
         if (this.runningState == State.RUNNING) {
 222  8
             this.stopTime = System.nanoTime();
 223  
         }
 224  12
         this.runningState = State.STOPPED;
 225  12
     }
 226  
 
 227  
     /**
 228  
      * <p>
 229  
      * Resets the stopwatch. Stops it if need be.
 230  
      * </p>
 231  
      * 
 232  
      * <p>
 233  
      * This method clears the internal values to allow the object to be reused.
 234  
      * </p>
 235  
      */
 236  
     public void reset() {
 237  4
         this.runningState = State.UNSTARTED;
 238  4
         this.splitState = SplitState.UNSPLIT;
 239  4
     }
 240  
 
 241  
     /**
 242  
      * <p>
 243  
      * Split the time.
 244  
      * </p>
 245  
      * 
 246  
      * <p>
 247  
      * This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected,
 248  
      * enabling {@link #unsplit()} to continue the timing from the original start point.
 249  
      * </p>
 250  
      * 
 251  
      * @throws IllegalStateException
 252  
      *             if the StopWatch is not running.
 253  
      */
 254  
     public void split() {
 255  4
         if (this.runningState != State.RUNNING) {
 256  2
             throw new IllegalStateException("Stopwatch is not running. ");
 257  
         }
 258  2
         this.stopTime = System.nanoTime();
 259  2
         this.splitState = SplitState.SPLIT;
 260  2
     }
 261  
 
 262  
     /**
 263  
      * <p>
 264  
      * Remove a split.
 265  
      * </p>
 266  
      * 
 267  
      * <p>
 268  
      * This method clears the stop time. The start time is unaffected, enabling timing from the original start point to
 269  
      * continue.
 270  
      * </p>
 271  
      * 
 272  
      * @throws IllegalStateException
 273  
      *             if the StopWatch has not been split.
 274  
      */
 275  
     public void unsplit() {
 276  6
         if (this.splitState != SplitState.SPLIT) {
 277  4
             throw new IllegalStateException("Stopwatch has not been split. ");
 278  
         }
 279  2
         this.splitState = SplitState.UNSPLIT;
 280  2
     }
 281  
 
 282  
     /**
 283  
      * <p>
 284  
      * Suspend the stopwatch for later resumption.
 285  
      * </p>
 286  
      * 
 287  
      * <p>
 288  
      * This method suspends the watch until it is resumed. The watch will not include time between the suspend and
 289  
      * resume calls in the total time.
 290  
      * </p>
 291  
      * 
 292  
      * @throws IllegalStateException
 293  
      *             if the StopWatch is not currently running.
 294  
      */
 295  
     public void suspend() {
 296  10
         if (this.runningState != State.RUNNING) {
 297  2
             throw new IllegalStateException("Stopwatch must be running to suspend. ");
 298  
         }
 299  8
         this.stopTime = System.nanoTime();
 300  8
         this.runningState = State.SUSPENDED;
 301  8
     }
 302  
 
 303  
     /**
 304  
      * <p>
 305  
      * Resume the stopwatch after a suspend.
 306  
      * </p>
 307  
      * 
 308  
      * <p>
 309  
      * This method resumes the watch after it was suspended. The watch will not include time between the suspend and
 310  
      * resume calls in the total time.
 311  
      * </p>
 312  
      * 
 313  
      * @throws IllegalStateException
 314  
      *             if the StopWatch has not been suspended.
 315  
      */
 316  
     public void resume() {
 317  6
         if (this.runningState != State.SUSPENDED) {
 318  4
             throw new IllegalStateException("Stopwatch must be suspended to resume. ");
 319  
         }
 320  2
         this.startTime += System.nanoTime() - this.stopTime;
 321  2
         this.runningState = State.RUNNING;
 322  2
     }
 323  
 
 324  
     /**
 325  
      * <p>
 326  
      * Get the time on the stopwatch.
 327  
      * </p>
 328  
      * 
 329  
      * <p>
 330  
      * This is either the time between the start and the moment this method is called, or the amount of time between
 331  
      * start and stop.
 332  
      * </p>
 333  
      * 
 334  
      * @return the time in milliseconds
 335  
      */
 336  
     public long getTime() {
 337  22
         return getNanoTime() / NANO_2_MILLIS;
 338  
     }
 339  
 
 340  
     /**
 341  
      * <p>
 342  
      * Get the time on the stopwatch in the specified TimeUnit.
 343  
      * </p>
 344  
      * 
 345  
      * <p>
 346  
      * This is either the time between the start and the moment this method is called, or the amount of time between
 347  
      * start and stop. The resulting time will be expressed in the desired TimeUnit with any remainder rounded down.
 348  
      * For example, if the specified unit is {@code TimeUnit.HOURS} and the stopwatch time is 59 minutes, then the
 349  
      * result returned will be {@code 0}.
 350  
      * </p>
 351  
      * 
 352  
      * @param timeUnit the unit of time, not null
 353  
      * @return the time in the specified TimeUnit, rounded down
 354  
      * @since 3.5
 355  
      */
 356  
     public long getTime(final TimeUnit timeUnit) {
 357  8
         return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS);
 358  
     }
 359  
 
 360  
     /**
 361  
      * <p>
 362  
      * Get the time on the stopwatch in nanoseconds.
 363  
      * </p>
 364  
      * 
 365  
      * <p>
 366  
      * This is either the time between the start and the moment this method is called, or the amount of time between
 367  
      * start and stop.
 368  
      * </p>
 369  
      * 
 370  
      * @return the time in nanoseconds
 371  
      * @since 3.0
 372  
      */
 373  
     public long getNanoTime() {
 374  30
         if (this.runningState == State.STOPPED || this.runningState == State.SUSPENDED) {
 375  22
             return this.stopTime - this.startTime;
 376  8
         } else if (this.runningState == State.UNSTARTED) {
 377  6
             return 0;
 378  2
         } else if (this.runningState == State.RUNNING) {
 379  2
             return System.nanoTime() - this.startTime;
 380  
         }
 381  0
         throw new RuntimeException("Illegal running state has occurred.");
 382  
     }
 383  
 
 384  
     /**
 385  
      * <p>
 386  
      * Get the split time on the stopwatch.
 387  
      * </p>
 388  
      * 
 389  
      * <p>
 390  
      * This is the time between start and latest split.
 391  
      * </p>
 392  
      * 
 393  
      * @return the split time in milliseconds
 394  
      * 
 395  
      * @throws IllegalStateException
 396  
      *             if the StopWatch has not yet been split.
 397  
      * @since 2.1
 398  
      */
 399  
     public long getSplitTime() {
 400  6
         return getSplitNanoTime() / NANO_2_MILLIS;
 401  
     }
 402  
     /**
 403  
      * <p>
 404  
      * Get the split time on the stopwatch in nanoseconds.
 405  
      * </p>
 406  
      * 
 407  
      * <p>
 408  
      * This is the time between start and latest split.
 409  
      * </p>
 410  
      * 
 411  
      * @return the split time in nanoseconds
 412  
      * 
 413  
      * @throws IllegalStateException
 414  
      *             if the StopWatch has not yet been split.
 415  
      * @since 3.0
 416  
      */
 417  
     public long getSplitNanoTime() {
 418  6
         if (this.splitState != SplitState.SPLIT) {
 419  2
             throw new IllegalStateException("Stopwatch must be split to get the split time. ");
 420  
         }
 421  4
         return this.stopTime - this.startTime;
 422  
     }
 423  
 
 424  
     /**
 425  
      * Returns the time this stopwatch was started.
 426  
      * 
 427  
      * @return the time this stopwatch was started
 428  
      * @throws IllegalStateException
 429  
      *             if this StopWatch has not been started
 430  
      * @since 2.4
 431  
      */
 432  
     public long getStartTime() {
 433  8
         if (this.runningState == State.UNSTARTED) {
 434  4
             throw new IllegalStateException("Stopwatch has not been started");
 435  
         }
 436  
         // System.nanoTime is for elapsed time
 437  4
         return this.startTimeMillis;
 438  
     }
 439  
 
 440  
     /**
 441  
      * <p>
 442  
      * Gets a summary of the time that the stopwatch recorded as a string.
 443  
      * </p>
 444  
      * 
 445  
      * <p>
 446  
      * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
 447  
      * </p>
 448  
      * 
 449  
      * @return the time as a String
 450  
      */
 451  
     @Override
 452  
     public String toString() {
 453  2
         return DurationFormatUtils.formatDurationHMS(getTime());
 454  
     }
 455  
 
 456  
     /**
 457  
      * <p>
 458  
      * Gets a summary of the split time that the stopwatch recorded as a string.
 459  
      * </p>
 460  
      * 
 461  
      * <p>
 462  
      * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
 463  
      * </p>
 464  
      * 
 465  
      * @return the split time as a String
 466  
      * @since 2.1
 467  
      */
 468  
     public String toSplitString() {
 469  2
         return DurationFormatUtils.formatDurationHMS(getSplitTime());
 470  
     }
 471  
 
 472  
     /**
 473  
      * <p>
 474  
      * The method is used to find out if the StopWatch is started. A suspended
 475  
      * StopWatch is also started watch.
 476  
      * </p>
 477  
      *
 478  
      * @return boolean
 479  
      *             If the StopWatch is started.
 480  
      * @since 3.2
 481  
      */
 482  
     public boolean isStarted() {
 483  10
         return runningState.isStarted();
 484  
     }
 485  
 
 486  
     /**
 487  
      * <p>
 488  
      * This method is used to find out whether the StopWatch is suspended.
 489  
      * </p>
 490  
      *
 491  
      * @return boolean
 492  
      *             If the StopWatch is suspended.
 493  
      * @since 3.2
 494  
      */
 495  
     public boolean isSuspended() {
 496  8
         return runningState.isSuspended();
 497  
     }
 498  
 
 499  
     /**
 500  
      * <p>
 501  
      * This method is used to find out whether the StopWatch is stopped. The
 502  
      * stopwatch which's not yet started and explicitly stopped stopwatch is
 503  
      * considered as stopped.
 504  
      * </p>
 505  
      *
 506  
      * @return boolean
 507  
      *             If the StopWatch is stopped.
 508  
      * @since 3.2
 509  
      */
 510  
     public boolean isStopped() {
 511  8
         return runningState.isStopped();
 512  
     }    
 513  
 
 514  
 }