001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.util;
007    
008    /**
009    Allows measuring the start and stop time and (hence the elapsed time) of
010    some event. ("event" meaning something of interest, for example, a method
011    call). Can be started/stopped repeatedly. (cumulative time across all such
012    start/stops are available via the {@link #cumulativeTime} method).
013    <p>
014    All times are in <i>milliseconds</i>. 
015    <p>
016    Thread Safety: This class is <b>not</b> threadsafe and it's method
017    do <b>not</b> acquire any locks to reduce any time skew due to
018    lock acquisition. Multiple threads should use separate Watch
019    objects or alternatively, higher level synchronization.
020    <p>
021    Note: This class <i>used</i> to be called "Timer" but changed to Watch to 
022    avoid an annoying name conflict with <code>java.util.Timer</code>
023    
024    @author   hursh jain
025    @version  1.0, 10/19/2001
026    @see    NanoWatch A higher nano-second precision watch.
027    */
028    public class Watch
029    {
030          String    myname;
031    volatile  boolean   running = false;
032          long    startTime = -1;
033          long    stopTime;
034          long    cumulativeTime;
035    
036    public Watch(String name) {
037      this.myname = name;
038      }
039    
040    public Watch() {
041      this("DefaultWatch/" + Thread.currentThread().getName());
042      }
043    
044    /** 
045    Start measuring time. Call this method just before calling the
046    method/code to be instrumented. This method does <b>not</b> reset the
047    Watch, so the reset method should be called before calling this method
048    <b>again</b>. Returns <tt>this</tt> so we can say:
049    <blockquote>
050    <tt>Watch w = new Watch().start()</tt>
051    </blockquote>
052    */
053    public Watch start() {
054      startTime = System.currentTimeMillis(); 
055      running = true;
056      return this;
057      }
058      
059    /** Stop measuring the time */
060    public void stop() {
061      stopTime = System.currentTimeMillis();
062      cumulativeTime += (stopTime - startTime);
063      running = false;
064      }
065    
066    /** 
067    Returns the time elapsed since the Watch was started. If the watch was
068    started and stopped, returns the time (in milliseconds) between the 
069    start/stop interval.
070    
071    @throws RuntimeException  if the watch was never started before calling
072                  this method.
073    */
074    public long time() 
075      {
076      if (startTime == -1)
077        throw new RuntimeException("You need to start the watch at least once before calling this method");
078        
079      if (running)
080        return System.currentTimeMillis() - startTime;
081      
082      return  stopTime - startTime;
083      }
084    
085    /** 
086    This method is an alias for {@link #time} method.
087    */
088    public long getTime() 
089      {
090      return time();
091      }
092    
093    /** 
094    Useful for showing the elapsed time in seconds. Intervals between,
095    (<code>0 - 499</code> milliseconds are rounded down and
096    <code>500 - 999</code> milliseconds are rounded up).
097    */
098    public long timeInSeconds() 
099      {
100      final long time = getTime();
101      
102      if (time < 500)  return 0;
103      if (time <= 1000) return 1;
104      
105      long quotient  = time / 1000;
106      long remainder = time % 1000;
107      return quotient + ((remainder < 500) ? 0 : 1);
108      }
109    
110    /** 
111    This method is an alias for {@link #timeInSeconds} method.
112    */
113    public long getTimeInSeconds() 
114      {
115      return timeInSeconds();
116      }
117    
118    /** 
119    This method is an alias for {@link #timeInSeconds} method.
120    */
121    public long seconds() 
122      {
123      return timeInSeconds();
124      }
125    
126    /** 
127    This method is an alias for {@link #timeInSeconds} method.
128    */
129    public long elapsed() 
130      {
131      return timeInSeconds();
132      }
133    
134    
135    /** 
136    This method is an alias for {@link #timeInSeconds} method.
137    */
138    public long timeSeconds() 
139      {
140      return timeInSeconds();
141      }
142    
143    
144    /** 
145    This method is an alias for {@link #getTimeInMillis} method.
146    */
147    public double timeMillis() 
148      {
149      return getTimeInMillis();
150      }
151    
152    
153    /**
154    Useful in NanoWatch and other subclasses. Not really useful here, returns
155    the same value as {@link #getTime}/{@link #time} functions instead. 
156    <p>
157    This method is defined here so all watches have a common interface and
158    any instance can be bound to a variable of type <code>Watch</code>.
159    */
160    public double getTimeInMillis()
161      {
162      return getTime() * 1.0D;
163      }
164    
165    /** 
166    Returns the total time recorded by this Watch (across several starts/stops)
167    */
168    public long cumulativeTime() 
169      {
170      if (! running) 
171        return cumulativeTime;
172      
173      return cumulativeTime + (System.currentTimeMillis() - startTime);
174      }
175    
176    /** 
177    Reset all values to zero. This method should be called before this object is
178    used <b>again</b>. Does not restart the timer, call start again when start the
179    timer.
180    */
181    public void reset() 
182      {
183      startTime = stopTime =  0;
184      }
185    
186    
187    /** 
188    Reset all values to zero and restarts the timer.
189    */
190    public void restart() 
191      {
192      reset();
193      start();
194      }
195    
196    /** Is the Watch currently running ? */
197    public boolean isRunning() {
198      return running;
199      }
200    
201    /** Get the start time (in milliseconds since Jan 1, 1970)*/
202    protected long getStart() {
203      return startTime;
204      }
205    
206    /** Get the stop time, (in milliseconds since Jan 1, 1970) */
207    protected long getStop() {
208      return stopTime;
209      }
210    
211    /** 
212    Describes the current state of this watch. The exact details of said
213    description are unspecified and subject to change.
214    */  
215    public String toString() 
216      {
217      String str = myname;
218    
219      str += ": Cum.Time=[" + cumulativeTime() + " ms]" + 
220          "; Start=[" + startTime + "]";
221      
222      if (! running) 
223        str +=  "; Stop=[" + stopTime + "]";
224      else 
225        str += "; Elapsed=[" + time() + " ms]";
226        
227      return str;
228      }
229    
230    public static void main(String[] args)
231      {
232      fc.util.Watch t1 = new fc.util.Watch("Watch 1");
233      t1.start();
234      
235      new Thread(new Runnable() { 
236        public void run() {
237          try { 
238            Watch t2 = new Watch();
239            t2.start(); Thread.currentThread().sleep(20); t2.stop(); 
240            System.out.println("t2.toString():" + t2);
241            } 
242          catch (Exception e) { e.printStackTrace(); }
243          } 
244        }).start();
245      
246      //following should return -1
247      System.out.println("Watch 1, total time taken:" + t1.time());
248    
249      System.out.println("Watch 1, time=" + t1.time());
250      System.out.println("Watch 1, before-being-stopped, toString():" + t1);
251      t1.stop();
252      System.out.println("Watch 1, is running ? " + t1.isRunning() );
253      System.out.println("Watch 1, after-being-stopped, toString():" + t1);
254      System.out.println("Watch 1, elapsed time:" + t1.time());
255      System.out.println("Watch 1, cumulative time taken:" + t1.cumulativeTime());
256      System.out.println("Watch 1, elapsed time:" + t1.time());
257    
258      new Thread(new Runnable() { 
259        public void run() {
260          try { 
261            Watch t2 = new Watch();
262            t2.start(); 
263            Thread.currentThread().sleep(250); 
264            t2.stop(); 
265            System.out.println("After sleeping 250ms: time in seconds:  " + t2.getTimeInSeconds());
266            t2.start();
267            Thread.currentThread().sleep(500); 
268            System.out.println("After sleeping 750ms: time in seconds:  " + t2.getTimeInSeconds());
269            Thread.currentThread().sleep(500); 
270            System.out.println("After sleeping 1250ms: time in seconds: " + t2.getTimeInSeconds());
271            Thread.currentThread().sleep(500); 
272            System.out.println("After sleeping 1750ms: time in seconds: " + t2.getTimeInSeconds());
273            Thread.currentThread().sleep(1000); 
274            System.out.println("After sleeping 2750ms: time in seconds: " + t2.getTimeInSeconds());
275            } 
276          catch (Exception e) { e.printStackTrace(); }
277          } 
278        }).start();
279      }
280    
281    } //~class Watch