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> 23 * <code>StopWatch</code> provides a convenient API for timings. 24 * </p> 25 * 26 * <p> 27 * To start the watch, call {@link #start()}. At this point you can: 28 * </p> 29 * <ul> 30 * <li>{@link #split()} the watch to get the time whilst the watch continues in 31 * the background. {@link #unsplit()} will remove the effect of the split. At 32 * this point, these three options are available again.</li> 33 * <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the 34 * watch to continue. Any time between the suspend and resume will not be 35 * counted in the total. At this point, these three options are available 36 * again.</li> 37 * <li>{@link #stop()} the watch to complete the timing session.</li> 38 * </ul> 39 * 40 * <p> 41 * It is intended that the output methods {@link #toString()} and 42 * {@link #getTime()} should only be called after stop, split or suspend, 43 * however a suitable result will be returned at other points. 44 * </p> 45 * 46 * <p> 47 * NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you 48 * cannot now call stop before start, resume before suspend or unsplit before 49 * split. 50 * </p> 51 * 52 * <p> 53 * 1. split(), suspend(), or stop() cannot be invoked twice <br> 54 * 2. unsplit() may only be called if the watch has been split()<br> 55 * 3. resume() may only be called if the watch has been suspend()<br> 56 * 4. start() cannot be called twice without calling reset() 57 * </p> 58 * 59 * @author Henri Yandell 60 * @author Stephen Colebourne 61 * @since 2.0 62 * @version $Id$ 63 */ 64 public class StopWatch { 65 66 // running states 67 private static final int STATE_UNSTARTED = 0; 68 private static final int STATE_RUNNING = 1; 69 private static final int STATE_STOPPED = 2; 70 private static final int STATE_SUSPENDED = 3; 71 72 // split state 73 private static final int STATE_UNSPLIT = 10; 74 private static final int STATE_SPLIT = 11; 75 76 /** 77 * The current running state of the StopWatch. 78 */ 79 private int runningState = STATE_UNSTARTED; 80 81 /** 82 * Whether the stopwatch has a split time recorded. 83 */ 84 private int splitState = STATE_UNSPLIT; 85 86 /** 87 * The start time. 88 */ 89 private long startTime = -1; 90 /** 91 * The stop time. 92 */ 93 private long stopTime = -1; 94 95 /** 96 * <p> 97 * Constructor. 98 * </p> 99 */ 100 public StopWatch() { 101 // nothing to do 102 } 103 104 /** 105 * <p> 106 * Start the stopwatch. 107 * </p> 108 * 109 * <p> 110 * This method starts a new timing session, clearing any previous values. 111 * </p> 112 * 113 * @throws IllegalStateException if the StopWatch is already running. 114 */ 115 public void start() { 116 if (this.runningState == STATE_STOPPED) { 117 throw new IllegalStateException("Stopwatch must be reset before being restarted. "); 118 } 119 if (this.runningState != STATE_UNSTARTED) { 120 throw new IllegalStateException("Stopwatch already started. "); 121 } 122 stopTime = -1; 123 startTime = System.currentTimeMillis(); 124 this.runningState = STATE_RUNNING; 125 } 126 127 /** 128 * <p> 129 * Stop the stopwatch. 130 * </p> 131 * 132 * <p> 133 * This method ends a new timing session, allowing the time to be retrieved. 134 * </p> 135 * 136 * @throws IllegalStateException if the StopWatch is not running. 137 */ 138 public void stop() { 139 if (this.runningState != STATE_RUNNING && this.runningState != STATE_SUSPENDED) { 140 throw new IllegalStateException("Stopwatch is not running. "); 141 } 142 stopTime = System.currentTimeMillis(); 143 this.runningState = STATE_STOPPED; 144 } 145 146 /** 147 * <p> 148 * Resets the stopwatch. Stops it if need be. 149 * </p> 150 * 151 * <p> 152 * This method clears the internal values to allow the object to be reused. 153 * </p> 154 */ 155 public void reset() { 156 this.runningState = STATE_UNSTARTED; 157 this.splitState = STATE_UNSPLIT; 158 startTime = -1; 159 stopTime = -1; 160 } 161 162 /** 163 * <p> 164 * Split the time. 165 * </p> 166 * 167 * <p> 168 * This method sets the stop time of the watch to allow a time to be extracted. 169 * The start time is unaffected, enabling {@link #unsplit()} to continue the 170 * timing from the original start point. 171 * </p> 172 * 173 * @throws IllegalStateException if the StopWatch is not running. 174 */ 175 public void split() { 176 if (this.runningState != STATE_RUNNING) { 177 throw new IllegalStateException("Stopwatch is not running. "); 178 } 179 stopTime = System.currentTimeMillis(); 180 this.splitState = STATE_SPLIT; 181 } 182 183 /** 184 * <p> 185 * Remove a split. 186 * </p> 187 * 188 * <p> 189 * This method clears the stop time. The start time is unaffected, enabling 190 * timing from the original start point to continue. 191 * </p> 192 * 193 * @throws IllegalStateException if the StopWatch has not been split. 194 */ 195 public void unsplit() { 196 if (this.splitState != STATE_SPLIT) { 197 throw new IllegalStateException("Stopwatch has not been split. "); 198 } 199 stopTime = -1; 200 this.splitState = STATE_UNSPLIT; 201 } 202 203 /** 204 * <p> 205 * Suspend the stopwatch for later resumption. 206 * </p> 207 * 208 * <p> 209 * This method suspends the watch until it is resumed. The watch will not 210 * include time between the suspend and resume calls in the total time. 211 * </p> 212 * 213 * @throws IllegalStateException if the StopWatch is not currently running. 214 */ 215 public void suspend() { 216 if (this.runningState != STATE_RUNNING) { 217 throw new IllegalStateException("Stopwatch must be running to suspend. "); 218 } 219 stopTime = System.currentTimeMillis(); 220 this.runningState = STATE_SUSPENDED; 221 } 222 223 /** 224 * <p> 225 * Resume the stopwatch after a suspend. 226 * </p> 227 * 228 * <p> 229 * This method resumes the watch after it was suspended. The watch will not 230 * include time between the suspend and resume calls in the total time. 231 * </p> 232 * 233 * @throws IllegalStateException if the StopWatch has not been suspended. 234 */ 235 public void resume() { 236 if (this.runningState != STATE_SUSPENDED) { 237 throw new IllegalStateException("Stopwatch must be suspended to resume. "); 238 } 239 startTime += System.currentTimeMillis() - stopTime; 240 stopTime = -1; 241 this.runningState = STATE_RUNNING; 242 } 243 244 /** 245 * Get the time on the stopwatch. 246 * 247 * This is either the time between the start and the moment this method is 248 * called, or the amount of time between start and stop. 249 * 250 * @return the time in milliseconds 251 */ 252 public long getTime() { 253 if (this.runningState == STATE_STOPPED || this.runningState == STATE_SUSPENDED) { 254 return this.stopTime - this.startTime; 255 } else if (this.runningState == STATE_UNSTARTED) { 256 return 0; 257 } else if (this.runningState == STATE_RUNNING) { 258 return System.currentTimeMillis() - this.startTime; 259 } 260 throw new RuntimeException("Illegal running state has occured. "); 261 } 262 263 /** 264 * Get the split time on the stopwatch. 265 * 266 * This is the time between start and latest split. 267 * 268 * @return the split time in milliseconds 269 * @throws IllegalStateException if the StopWatch has not yet been split. 270 * @since 2.1 271 */ 272 public long getSplitTime() { 273 if (this.splitState != STATE_SPLIT) { 274 throw new IllegalStateException("Stopwatch must be split to get the split time. "); 275 } 276 return this.stopTime - this.startTime; 277 } 278 279 /** 280 * Gets a summary of the time that the stopwatch recorded as a string. 281 * 282 * @return the time as a String 283 */ 284 public String toString() { 285 return getTime() + "ms"; 286 } 287 }