Open Source Repository

Home /jodd/jodd-3.3.2 | Repository Home



jodd/datetime/JStopWatch.java
// Copyright (c) 2003-2012, Jodd Team (jodd.org). All Rights Reserved.

package jodd.datetime;

import java.util.List;
import java.util.ArrayList;

/**
 * Nice thread-aware stopwatch that supports time spans, cumulative times and laps.
 * Useful for all kind of profiling, time measurements etc.
 */
public class JStopWatch {

  private static final ThreadLocal<JStopWatch> THREAD_LOCAL = new ThreadLocal<JStopWatch>();

  /**
   * Optional stopwatch name.
   */
  protected String name;
  /**
   * Last start time.
   */
  protected long startTime;
  /**
   * Last stop time.
   */
  protected long stopTime;
  /**
   * Last elapsed time.
   */
  protected long spanTime;
  /**
   * Cumulative elapsed time.
   */
  protected long totalTime;
  /**
   * Running flag.
   */
  protected boolean running;

  // ---------------------------------------------------------------- thread

  /**
   * Puts current stopwatch in current thread.
   */
  protected void assignToCurrentThread() {
    THREAD_LOCAL.set(this);
  }

  /**
   * Returns thread-assigned stop watch.
   */
  public static JStopWatch getThreadStopWatch() {
    return THREAD_LOCAL.get();
  }

  // ---------------------------------------------------------------- ctors

  /**
   * Starts the stopwatch.
   */
  public JStopWatch() {
    this(false);
  }

  public JStopWatch(boolean putInThread) {
    this("#jStopWatch", putInThread);
  }

  /**
   * Starts the named stopwatch.
   */
  public JStopWatch(String name) {
    this(name, false);
  }

  /**
   * Starts the stopwatch.
   */
  public JStopWatch(String name, boolean assignToCurrentThread) {
    this.name = name;
    if (assignToCurrentThread) {
      assignToCurrentThread();
    }
    start();
  }

  /**
   * Returns stopwatch name.
   */
  public String getName() {
    return name;
  }

  /**
   * Returns <code>true</code> if stopwatch is running.
   */
  public boolean isRunning() {
    return running;
  }

  // ---------------------------------------------------------------- basic

  /**
   * Starts the stopwatch. {@link #stop()} must be called prior to restart.
   * Returns starting time in milliseconds.
   */
  public long start() {
    if (running == false) {
      startTime = System.currentTimeMillis();
      running = true;
    }
    return startTime;
  }

  /**
   * Restarts the stopwatch.
   */
  public long restart() {
    startTime = System.currentTimeMillis();
    running = true;
    return startTime;
  }

  /**
   * Stops the stopwatch if running. Returns span time.
   * If laps are used, marks the last lap.
   */
  public long stop() {
    if (running == true) {
      stopTime = System.currentTimeMillis();
      if (laps != null) {
        lap(stopTime);
      }
      spanTime = stopTime - startTime;
      totalTime += stopTime - startTime;
      running = false;
    }
    return spanTime;
  }
  
  /**
   * Returns total elapsed time from the {@link #start()} in ms.
   */
  public long elapsed() {
    return System.currentTimeMillis() - startTime;
  }

  /**
   * Stops the stopwatch and returns total cumulative time in ms.
   */
  public long total() {
    stop();
        return totalTime;
  }

  /**
   * Stops the stopwatch and returns total time span for last
   * start-stop sequence.
   */
  public long span() {
    stop();
    return spanTime;
  }


  // ---------------------------------------------------------------- laps

  /**
   * List of all laps. Contains long arrays in following format:
   <ul>
   <li>lap time - current lap time,</li>
   <li>lap span time - elapsed time from start,</li>
   <li>lap millis - lap milliseconds. 
   </ul>
   */
  protected List<long[]> laps;

  /**
   * Marks a lap and returns its length. May be called only while stop watch is running.
   */
  public long lap() {
    return lap(System.currentTimeMillis());
  }

  protected long lap(long lap) {
    if (running == false) {
      return 0;
    }
    long lapSpanTime = lap - startTime;
    long lapTime;
    if (laps == null) {
      lapTime = lapSpanTime;
      laps = new ArrayList<long[]>();
    else {
      long[] previous = laps.get(laps.size() 1);
      lapTime = lap - previous[2];
    }
    laps.add(new long[] {lapTime, lapSpanTime, lap});
    return lapTime;
  }

  /**
   * Returns the total number of laps up to this moment.
   */
  public int totalLaps() {
    if (laps == null) {
      return 0;
    }
    return laps.size();
  }

  /**
   * Returns lap times for 1-based lap index.
   * Returns <code>null</code> if laps are not used or if index is invalid.
   */
  public long[] getLapTimes(int index) {
    if (laps == null) {
      return null;
    }
    if ((index <= 0|| (index > laps.size())) {
      return null;
    }
    return laps.get(index - 1);
  }

  // ---------------------------------------------------------------- output

  /**
   * Returns total elapsed time as formatted string from the last start.
   */
  @Override
  public String toString() {
    long elapsed = elapsed();
    StringBuilder sb = new StringBuilder();
    sb.append("JStopWatch ").append(name).append(running ? " is running." "").append('\n');
    if (running == true) {
      sb.append("elapsed: ").append(formatTimeSpan(elapsed));
    else {
      if (spanTime != totalTime) {
        sb.append("span:  ").append(formatTimeSpan(spanTime)).append('\n');
      }
      sb.append("total: ").append(formatTimeSpan(totalTime));
    }
    if (laps != null) {
      if (laps.isEmpty() == false) {
        sb.append('\n');
      }
      for (int i = 0; i < laps.size(); i++) {
        long[] longs = laps.get(i);
        sb.append("  lap #").append(i + 1).append(':').append('\t');
        sb.append(formatTimeSpan(longs[0])).append('\t');
        sb.append(formatTimeSpan(longs[1])).append('\n');
      }
    }
    return sb.toString();
  }

  /**
   * Formats time spans.
   */
  public static String formatTimeSpan(long millis) {
    long seconds = 0;
    long minutes = 0;
    long hours = 0;

    if (millis > 1000) {
      seconds = millis / 1000;
      millis %= 1000;
    }
    if (seconds > 60) {
      minutes = seconds / 60;
      seconds %= 60;
    }
    if (minutes > 60) {
      hours = minutes / 60;
      minutes %= 60;
    }

    StringBuilder result = new StringBuilder(20);
    boolean out = false;
    if (hours > 0) {
      result.append(hours).append(':');
      out = true;
    }
    if ((out == true|| (minutes > 0)) {
      if (minutes < 10) {
        result.append('0');
      }
      result.append(minutes).append(':');
    }

    if (seconds < 10) {
      result.append('0');
    }
    result.append(seconds).append('.');

    if (millis < 10) {
      result.append('0');
    }
    if (millis < 100) {
      result.append('0');
    }
    result.append(millis);
    return result.toString();
  }
}