Files
JavaClasses/src/main/java/com/mattrixwv/Stopwatch.java
2024-08-11 21:31:00 -04:00

285 lines
8.0 KiB
Java

//JavaClasses/src/main/java/mattrixwv/Stopwatch.java
//Matthew Ellison (Mattrixwv)
// Created: 03-01-19
//Modified: 08-11-24
//This file contains a class that is used to time the execution time of other programs
/*
Copyright (C) 2024 Matthew Ellison
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.mattrixwv;
import com.mattrixwv.exceptions.InvalidResult;
/**
* A simple stopwatch class to measure elapsed time in various units of resolution.
*
* <p>
* This class provides methods to start, stop, and reset a stopwatch, as well as retrieve the
* elapsed time in nanoseconds, microseconds, milliseconds, seconds, minutes, and hours. It
* also includes functionality to get the elapsed time as a formatted string in the most
* appropriate time unit.
* </p>
*/
public class Stopwatch{
/**
* The start time of the stopwatch in nanoseconds.
*/
private Long startTime;
/**
* The stop time of the stopwatch in nanoseconds.
*/
private Long stopTime;
//?Constructor makes sure all values are set to defaults
/**
* Constructs a new Stopwatch and initializes the start and stop times to {@code null}.
* This ensures that incorrect function calling order can be detected easily.
*/
public Stopwatch(){
//Make sure both values are null so it is easier to detect incorrect function calling order
startTime = null;
stopTime = null;
}
//?Returns a long with the elapsed time in nanoseconds. Used by other functions to get the time before converting it to the correct resolution
/**
* Returns the elapsed time in nanoseconds.
* <p>
* If the stopwatch has not been started, it returns {@code 0L}. If the stopwatch has
* been started but not stopped, it returns the time elapsed since it was started.
* </p>
*
* @return the elapsed time in nanoseconds
*/
private Long getTime(){
if(startTime == null){
return 0L;
}
else if(stopTime == null){
return System.nanoTime() - startTime;
}
else{
return stopTime - startTime;
}
}
//?An enum that helps keep track of how many times the time has been reduced in the getStr function
/**
* Enum to represent the time resolution for formatting the time string.
*/
private enum TIME_RESOLUTION{ NANOSECOND, MICROSECOND, MILLISECOND, SECOND, MINUTE, HOUR, ERROR }
//?Simulates starting a stopwatch by saving the time
/**
* Starts the stopwatch by recording the current time in nanoseconds.
* <p>
* If the stopwatch was already running, the previous start time is overwritten.
* </p>
*/
public void start(){
//Make sure the stop time is reset to 0
stopTime = null;
//Get the time as close to returning from the function as possible
startTime = System.nanoTime();
}
//?Simulates stopping a stopwatch by saving the time
/**
* Stops the stopwatch by recording the current time in nanoseconds.
* <p>
* If the stopwatch has not been started, this method has no effect.
* </p>
*/
public void stop(){
//Set the stopTime as close to call time as possible
stopTime = System.nanoTime();
//If the startTime has not been set then reset stopTime
if(startTime == null){
stopTime = null;
}
}
//?Resets all variables in the stopwatch
/**
* Resets the stopwatch, clearing the start and stop times.
* <p>
* After calling this method, the stopwatch needs to be started again before measuring
* elapsed time.
* </p>
*/
public void reset(){
//Make sure all variables are reset correctly
startTime = null;
stopTime = null;
}
//?Returns the time in nanoseconds
/**
* Returns the elapsed time in nanoseconds.
*
* @return the elapsed time in nanoseconds as a {@code double}
*/
public double getNano(){
return getTime().doubleValue();
}
//?Returns the time in microseconds
/**
* Returns the elapsed time in microseconds.
*
* @return the elapsed time in microseconds as a {@code double}
*/
public double getMicro(){
return getTime().doubleValue() / 1000D;
}
//?Returns the time in milliseconds
/**
* Returns the elapsed time in milliseconds.
*
* @return the elapsed time in milliseconds as a {@code double}
*/
public double getMilli(){
return getTime().doubleValue() / 1000000D;
}
//?Returns the time in seconds
/**
* Returns the elapsed time in seconds.
*
* @return the elapsed time in seconds as a {@code double}
*/
public double getSecond(){
return getTime().doubleValue() / 1000000000D;
}
//?Returns the time in minutes
/**
* Returns the elapsed time in minutes.
*
* @return the elapsed time in minutes as a {@code double}
*/
public double getMinute(){
return getTime().doubleValue() / 60000000000D;
}
//?Returns the time in hours
/**
* Returns the elapsed time in hours.
*
* @return the elapsed time in hours as a {@code double}
*/
public double getHour(){
return getTime().doubleValue() / 3600000000000D;
}
//?Returns the time as a string at the 'best' resolution. (Goal is xxx.xxx)
/**
* Returns the elapsed time as a formatted string with the most appropriate resolution.
* <p>
* The method automatically selects the best time resolution to display the time in a
* human-readable format.
* </p>
*
* @return a formatted string representing the elapsed time
* @throws InvalidResult if the time resolution is invalid
*/
public String getStr() throws InvalidResult{
//Get the current duration from time
return getStr(getTime().doubleValue());
}
/**
* Returns a formatted string representing the given time in nanoseconds with the most
* appropriate resolution.
*
* @param nanoseconds the time in nanoseconds to format
* @return a formatted string representing the given time
* @throws InvalidResult if the time resolution is invalid
*/
public static String getStr(double nanoseconds) throws InvalidResult{
Double duration = nanoseconds;
//Reduce the number to the appropriate number of digits. (xxx.x).
//This loop works down to seconds
TIME_RESOLUTION resolution;
for(resolution = TIME_RESOLUTION.NANOSECOND;(resolution.ordinal() < TIME_RESOLUTION.SECOND.ordinal()) && (duration >= 1000);resolution = TIME_RESOLUTION.values()[resolution.ordinal() + 1]){
duration /= 1000;
}
//Check if the duration needs reduced to minutes
if((duration >= 120) && (resolution == TIME_RESOLUTION.SECOND)){
//Reduce to minutes
duration /= 60;
resolution = TIME_RESOLUTION.values()[resolution.ordinal() + 1];
//Check if the duration needs reduced to hours
if(duration >= 60){
//Reduce to hours
duration /= 60;
resolution = TIME_RESOLUTION.values()[resolution.ordinal() + 1];
}
}
if(duration < 0){
resolution = TIME_RESOLUTION.ERROR;
}
//Turn the number into a string
int durationFraction = (int)Math.round(((duration % 1) * 1000));
String time = String.format("%d.%03d", duration.intValue(), durationFraction);
//Tack on the appropriate suffix for resolution
switch(resolution){
case NANOSECOND: time += " nanoseconds"; break;
case MICROSECOND: time += " microseconds"; break;
case MILLISECOND: time += " milliseconds"; break;
case SECOND: time += " seconds"; break;
case MINUTE: time += " minutes"; break;
case HOUR: time += " hours"; break;
case ERROR:
default: throw new InvalidResult("timeResolution was invalid");
}
//Return the string
return time;
}
/**
* Returns a string representation of the elapsed time in the most appropriate resolution.
*
* @return a string representation of the elapsed time
*/
@Override
public String toString(){
return getStr();
}
}