001    package hirondelle.web4j.util;
002    
003    /**
004     Allows timing of the execution of any block of code.
005    
006     Example use case:
007    <PRE>
008    Stopwatch stopwatch = new Stopwatch();
009    stopwatch.start();
010    //..perform operations
011    stopwatch.stop();
012    
013    System.out.println("The reading on the stopwatch is: " + stopwatch);
014    
015    //reuse the same stopwatch again
016    //Note that there is no need to call a reset method.
017    stopwatch.start();
018    //..perform operations
019    stopwatch.stop();
020    
021    //perform a numeric comparison
022    if ( stopwatch.toValue() > 5 ) {
023      System.out.println("The reading is high: " + stopwatch);
024    }
025    </PRE>
026    
027     <P>The value on the stopwatch may be inspected at any time using the  
028     {@link #toString} and {@link #toValue} methods.
029     
030     <P><b>Example 2</b>
031     <br>To time the various steps in a long startup or initialization task, your code may take the form:
032    <PRE>
033    Stopwatch stopwatch = new Stopwatch();
034    stopwatch.start();
035    //..perform operation 1
036    log("Step 1 completed " + stopwatch + " after start.");
037    
038    //perform operation 2
039    log("Step 2 completed " + stopwatch + " after start.");
040    
041    //perform the last operation, operation 3
042    stopwatch.stop();
043    log("Final Step 3 completed " + stopwatch + " after start") ;
044    </PRE>
045    
046    <P><i>Implementation Note:</i></br>
047     This class uses {@link System#nanoTime()}, not {@link System#currentTimeMillis()}.
048    */
049    public final class Stopwatch {
050    
051      /**
052       Start the stopwatch.
053      
054       <P>You cannot call this method if the stopwatch is already started.
055      */
056      public void start(){
057        if ( fIsRunning ) {
058          throw new IllegalStateException("Must stop before calling start again.");
059        }
060        //reset both start and stop
061        fStart = getCurrentTime();
062        fStop = 0;
063        fIsRunning = true;
064      }
065    
066      /**
067       Stop the stopwatch.
068      
069       <P>You can only call this method if the stopwatch has been started.
070      */
071      public void stop() {
072        if ( !fIsRunning ) {
073          throw new IllegalStateException("Cannot stop if not currently running.");
074        }
075        fStop = getCurrentTime();
076        fIsRunning = false;
077      }
078    
079      /**
080       Return the current "reading" on the stopwatch, in milliseconds, in a format suitable for logging.
081       
082       <P>Example return value : '<tt>1089 ms</tt>', which indicates just over a second.
083      */
084      public String toString() {
085        StringBuilder result = new StringBuilder();
086        result.append( toValue() );
087        result.append(" ms");
088        return result.toString();
089      }
090    
091      /**
092       Return the current "reading" on the stopwatch in milliseconds, as a numeric type.
093      */
094      public long toValue() {
095        long result = fStop == 0 ? getCurrentTime() - fStart : fStop -fStart;
096        return result;
097      }
098    
099      // PRIVATE //
100      private long fStart;
101      private long fStop;
102      private boolean fIsRunning;
103      
104      /** Converts from nanoseconds to milliseconds. */
105      private static final int DIVISOR = 1000*1000;
106      
107      private long getCurrentTime(){
108        return System.nanoTime()/DIVISOR;
109      }
110    }