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  2
         final StopWatch sw = new StopWatch();
 73  2
         sw.start();
 74  2
         return sw;
 75  
     }
 76  
 
 77  
     /**
 78  
      * Enumeration type which indicates the status of stopwatch.
 79  
      */
 80  9
     private enum State {
 81  
 
 82  1
         UNSTARTED {
 83  
             @Override
 84  
             boolean isStarted() {
 85  1
                 return false;
 86  
             }
 87  
             @Override
 88  
             boolean isStopped() {
 89  1
                 return true;
 90  
             }
 91  
             @Override
 92  
             boolean isSuspended() {
 93  1
                 return false;
 94  
             }
 95  
         },
 96  1
         RUNNING {
 97  
             @Override
 98  
             boolean isStarted() {
 99  2
                 return true;
 100  
             }
 101  
             @Override
 102  
             boolean isStopped() {
 103  1
                 return false;
 104  
             }
 105  
             @Override
 106  
             boolean isSuspended() {
 107  1
                 return false;
 108  
             }
 109  
         },
 110  1
         STOPPED {
 111  
             @Override
 112  
             boolean isStarted() {
 113  1
                 return false;
 114  
             }
 115  
             @Override
 116  
             boolean isStopped() {
 117  1
                 return true;
 118  
             }
 119  
             @Override
 120  
             boolean isSuspended() {
 121  1
                 return false;
 122  
             }
 123  
         },
 124  1
         SUSPENDED {
 125  
             @Override
 126  
             boolean isStarted() {
 127  1
                 return true;
 128  
             }
 129  
             @Override
 130  
             boolean isStopped() {
 131  1
                 return false;
 132  
             }
 133  
             @Override
 134  
             boolean isSuspended() {
 135  1
                 return true;
 136  
             }
 137  
         };
 138  
 
 139  
         /**
 140  
          * <p>
 141  
          * The method is used to find out if the StopWatch is started. A suspended
 142  
          * StopWatch is also started watch.
 143  
          * </p>
 144  
 
 145  
          * @return boolean
 146  
          *             If the StopWatch is started.
 147  
          */
 148  
         abstract boolean isStarted();
 149  
 
 150  
         /**
 151  
          * <p>
 152  
          * This method is used to find out whether the StopWatch is stopped. The
 153  
          * stopwatch which's not yet started and explicitly stopped stopwatch is
 154  
          * considered as stopped.
 155  
          * </p>
 156  
          *
 157  
          * @return boolean
 158  
          *             If the StopWatch is stopped.
 159  
          */
 160  
         abstract boolean isStopped();
 161  
 
 162  
         /**
 163  
          * <p>
 164  
          * This method is used to find out whether the StopWatch is suspended.
 165  
          * </p>
 166  
          *
 167  
          * @return boolean
 168  
          *             If the StopWatch is suspended.
 169  
          */
 170  
         abstract boolean isSuspended();
 171  
     }
 172  
 
 173  
     /**
 174  
      * Enumeration type which indicates the split status of stopwatch.
 175  
      */
 176  3
     private enum SplitState {
 177  1
         SPLIT,
 178  1
         UNSPLIT
 179  
     }
 180  
     /**
 181  
      * The current running state of the StopWatch.
 182  
      */
 183  10
     private State runningState = State.UNSTARTED;
 184  
 
 185  
     /**
 186  
      * Whether the stopwatch has a split time recorded.
 187  
      */
 188  10
     private SplitState splitState = SplitState.UNSPLIT;
 189  
 
 190  
     /**
 191  
      * The start time.
 192  
      */
 193  
     private long startTime;
 194  
 
 195  
     /**
 196  
      * The start time in Millis - nanoTime is only for elapsed time so we
 197  
      * need to also store the currentTimeMillis to maintain the old
 198  
      * getStartTime API.
 199  
      */
 200  
     private long startTimeMillis;
 201  
 
 202  
     /**
 203  
      * The stop time.
 204  
      */
 205  
     private long stopTime;
 206  
 
 207  
     /**
 208  
      * <p>
 209  
      * Constructor.
 210  
      * </p>
 211  
      */
 212  
     public StopWatch() {
 213  10
         super();
 214  10
     }
 215  
 
 216  
     /**
 217  
      * <p>
 218  
      * Start the stopwatch.
 219  
      * </p>
 220  
      *
 221  
      * <p>
 222  
      * This method starts a new timing session, clearing any previous values.
 223  
      * </p>
 224  
      *
 225  
      * @throws IllegalStateException
 226  
      *             if the StopWatch is already running.
 227  
      */
 228  
     public void start() {
 229  12
         if (this.runningState == State.STOPPED) {
 230  1
             throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
 231  
         }
 232  11
         if (this.runningState != State.UNSTARTED) {
 233  1
             throw new IllegalStateException("Stopwatch already started. ");
 234  
         }
 235  10
         this.startTime = System.nanoTime();
 236  10
         this.startTimeMillis = System.currentTimeMillis();
 237  10
         this.runningState = State.RUNNING;
 238  10
     }
 239  
 
 240  
 
 241  
     /**
 242  
      * <p>
 243  
      * Stop the stopwatch.
 244  
      * </p>
 245  
      *
 246  
      * <p>
 247  
      * This method ends a new timing session, allowing the time to be retrieved.
 248  
      * </p>
 249  
      *
 250  
      * @throws IllegalStateException
 251  
      *             if the StopWatch is not running.
 252  
      */
 253  
     public void stop() {
 254  8
         if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) {
 255  2
             throw new IllegalStateException("Stopwatch is not running. ");
 256  
         }
 257  6
         if (this.runningState == State.RUNNING) {
 258  4
             this.stopTime = System.nanoTime();
 259  
         }
 260  6
         this.runningState = State.STOPPED;
 261  6
     }
 262  
 
 263  
     /**
 264  
      * <p>
 265  
      * Resets the stopwatch. Stops it if need be.
 266  
      * </p>
 267  
      *
 268  
      * <p>
 269  
      * This method clears the internal values to allow the object to be reused.
 270  
      * </p>
 271  
      */
 272  
     public void reset() {
 273  2
         this.runningState = State.UNSTARTED;
 274  2
         this.splitState = SplitState.UNSPLIT;
 275  2
     }
 276  
 
 277  
     /**
 278  
      * <p>
 279  
      * Split the time.
 280  
      * </p>
 281  
      *
 282  
      * <p>
 283  
      * This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected,
 284  
      * enabling {@link #unsplit()} to continue the timing from the original start point.
 285  
      * </p>
 286  
      *
 287  
      * @throws IllegalStateException
 288  
      *             if the StopWatch is not running.
 289  
      */
 290  
     public void split() {
 291  2
         if (this.runningState != State.RUNNING) {
 292  1
             throw new IllegalStateException("Stopwatch is not running. ");
 293  
         }
 294  1
         this.stopTime = System.nanoTime();
 295  1
         this.splitState = SplitState.SPLIT;
 296  1
     }
 297  
 
 298  
     /**
 299  
      * <p>
 300  
      * Remove a split.
 301  
      * </p>
 302  
      *
 303  
      * <p>
 304  
      * This method clears the stop time. The start time is unaffected, enabling timing from the original start point to
 305  
      * continue.
 306  
      * </p>
 307  
      *
 308  
      * @throws IllegalStateException
 309  
      *             if the StopWatch has not been split.
 310  
      */
 311  
     public void unsplit() {
 312  3
         if (this.splitState != SplitState.SPLIT) {
 313  2
             throw new IllegalStateException("Stopwatch has not been split. ");
 314  
         }
 315  1
         this.splitState = SplitState.UNSPLIT;
 316  1
     }
 317  
 
 318  
     /**
 319  
      * <p>
 320  
      * Suspend the stopwatch for later resumption.
 321  
      * </p>
 322  
      *
 323  
      * <p>
 324  
      * This method suspends the watch until it is resumed. The watch will not include time between the suspend and
 325  
      * resume calls in the total time.
 326  
      * </p>
 327  
      *
 328  
      * @throws IllegalStateException
 329  
      *             if the StopWatch is not currently running.
 330  
      */
 331  
     public void suspend() {
 332  5
         if (this.runningState != State.RUNNING) {
 333  1
             throw new IllegalStateException("Stopwatch must be running to suspend. ");
 334  
         }
 335  4
         this.stopTime = System.nanoTime();
 336  4
         this.runningState = State.SUSPENDED;
 337  4
     }
 338  
 
 339  
     /**
 340  
      * <p>
 341  
      * Resume the stopwatch after a suspend.
 342  
      * </p>
 343  
      *
 344  
      * <p>
 345  
      * This method resumes the watch after it was suspended. The watch will not include time between the suspend and
 346  
      * resume calls in the total time.
 347  
      * </p>
 348  
      *
 349  
      * @throws IllegalStateException
 350  
      *             if the StopWatch has not been suspended.
 351  
      */
 352  
     public void resume() {
 353  3
         if (this.runningState != State.SUSPENDED) {
 354  2
             throw new IllegalStateException("Stopwatch must be suspended to resume. ");
 355  
         }
 356  1
         this.startTime += System.nanoTime() - this.stopTime;
 357  1
         this.runningState = State.RUNNING;
 358  1
     }
 359  
 
 360  
     /**
 361  
      * <p>
 362  
      * Get the time on the stopwatch.
 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 milliseconds
 371  
      */
 372  
     public long getTime() {
 373  11
         return getNanoTime() / NANO_2_MILLIS;
 374  
     }
 375  
 
 376  
     /**
 377  
      * <p>
 378  
      * Get the time on the stopwatch in the specified TimeUnit.
 379  
      * </p>
 380  
      *
 381  
      * <p>
 382  
      * This is either the time between the start and the moment this method is called, or the amount of time between
 383  
      * start and stop. The resulting time will be expressed in the desired TimeUnit with any remainder rounded down.
 384  
      * For example, if the specified unit is {@code TimeUnit.HOURS} and the stopwatch time is 59 minutes, then the
 385  
      * result returned will be {@code 0}.
 386  
      * </p>
 387  
      *
 388  
      * @param timeUnit the unit of time, not null
 389  
      * @return the time in the specified TimeUnit, rounded down
 390  
      * @since 3.5
 391  
      */
 392  
     public long getTime(final TimeUnit timeUnit) {
 393  4
         return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS);
 394  
     }
 395  
 
 396  
     /**
 397  
      * <p>
 398  
      * Get the time on the stopwatch in nanoseconds.
 399  
      * </p>
 400  
      *
 401  
      * <p>
 402  
      * This is either the time between the start and the moment this method is called, or the amount of time between
 403  
      * start and stop.
 404  
      * </p>
 405  
      *
 406  
      * @return the time in nanoseconds
 407  
      * @since 3.0
 408  
      */
 409  
     public long getNanoTime() {
 410  15
         if (this.runningState == State.STOPPED || this.runningState == State.SUSPENDED) {
 411  11
             return this.stopTime - this.startTime;
 412  4
         } else if (this.runningState == State.UNSTARTED) {
 413  3
             return 0;
 414  1
         } else if (this.runningState == State.RUNNING) {
 415  1
             return System.nanoTime() - this.startTime;
 416  
         }
 417  0
         throw new RuntimeException("Illegal running state has occurred.");
 418  
     }
 419  
 
 420  
     /**
 421  
      * <p>
 422  
      * Get the split time on the stopwatch.
 423  
      * </p>
 424  
      *
 425  
      * <p>
 426  
      * This is the time between start and latest split.
 427  
      * </p>
 428  
      *
 429  
      * @return the split time in milliseconds
 430  
      *
 431  
      * @throws IllegalStateException
 432  
      *             if the StopWatch has not yet been split.
 433  
      * @since 2.1
 434  
      */
 435  
     public long getSplitTime() {
 436  3
         return getSplitNanoTime() / NANO_2_MILLIS;
 437  
     }
 438  
     /**
 439  
      * <p>
 440  
      * Get the split time on the stopwatch in nanoseconds.
 441  
      * </p>
 442  
      *
 443  
      * <p>
 444  
      * This is the time between start and latest split.
 445  
      * </p>
 446  
      *
 447  
      * @return the split time in nanoseconds
 448  
      *
 449  
      * @throws IllegalStateException
 450  
      *             if the StopWatch has not yet been split.
 451  
      * @since 3.0
 452  
      */
 453  
     public long getSplitNanoTime() {
 454  3
         if (this.splitState != SplitState.SPLIT) {
 455  1
             throw new IllegalStateException("Stopwatch must be split to get the split time. ");
 456  
         }
 457  2
         return this.stopTime - this.startTime;
 458  
     }
 459  
 
 460  
     /**
 461  
      * Returns the time this stopwatch was started.
 462  
      *
 463  
      * @return the time this stopwatch was started
 464  
      * @throws IllegalStateException
 465  
      *             if this StopWatch has not been started
 466  
      * @since 2.4
 467  
      */
 468  
     public long getStartTime() {
 469  4
         if (this.runningState == State.UNSTARTED) {
 470  2
             throw new IllegalStateException("Stopwatch has not been started");
 471  
         }
 472  
         // System.nanoTime is for elapsed time
 473  2
         return this.startTimeMillis;
 474  
     }
 475  
 
 476  
     /**
 477  
      * <p>
 478  
      * Gets a summary of the time that the stopwatch recorded as a string.
 479  
      * </p>
 480  
      *
 481  
      * <p>
 482  
      * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
 483  
      * </p>
 484  
      *
 485  
      * @return the time as a String
 486  
      */
 487  
     @Override
 488  
     public String toString() {
 489  1
         return DurationFormatUtils.formatDurationHMS(getTime());
 490  
     }
 491  
 
 492  
     /**
 493  
      * <p>
 494  
      * Gets a summary of the split time that the stopwatch recorded as a string.
 495  
      * </p>
 496  
      *
 497  
      * <p>
 498  
      * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
 499  
      * </p>
 500  
      *
 501  
      * @return the split time as a String
 502  
      * @since 2.1
 503  
      */
 504  
     public String toSplitString() {
 505  1
         return DurationFormatUtils.formatDurationHMS(getSplitTime());
 506  
     }
 507  
 
 508  
     /**
 509  
      * <p>
 510  
      * The method is used to find out if the StopWatch is started. A suspended
 511  
      * StopWatch is also started watch.
 512  
      * </p>
 513  
      *
 514  
      * @return boolean
 515  
      *             If the StopWatch is started.
 516  
      * @since 3.2
 517  
      */
 518  
     public boolean isStarted() {
 519  5
         return runningState.isStarted();
 520  
     }
 521  
 
 522  
     /**
 523  
      * <p>
 524  
      * This method is used to find out whether the StopWatch is suspended.
 525  
      * </p>
 526  
      *
 527  
      * @return boolean
 528  
      *             If the StopWatch is suspended.
 529  
      * @since 3.2
 530  
      */
 531  
     public boolean isSuspended() {
 532  4
         return runningState.isSuspended();
 533  
     }
 534  
 
 535  
     /**
 536  
      * <p>
 537  
      * This method is used to find out whether the StopWatch is stopped. The
 538  
      * stopwatch which's not yet started and explicitly stopped stopwatch is
 539  
      * considered as stopped.
 540  
      * </p>
 541  
      *
 542  
      * @return boolean
 543  
      *             If the StopWatch is stopped.
 544  
      * @since 3.2
 545  
      */
 546  
     public boolean isStopped() {
 547  4
         return runningState.isStopped();
 548  
     }
 549  
 
 550  
 }