Skip to content

Commit 61d73f8

Browse files
author
edelgadoh
committed
Adding labels to split StopWatch feature
1 parent f4762c3 commit 61d73f8

File tree

2 files changed

+371
-0
lines changed

2 files changed

+371
-0
lines changed

src/main/java/org/apache/commons/lang3/time/StopWatch.java

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
import java.time.Duration;
2121
import java.time.Instant;
22+
import java.util.ArrayList;
23+
import java.util.List;
2224
import java.util.Objects;
2325
import java.util.concurrent.TimeUnit;
2426
import java.util.function.Supplier;
@@ -248,6 +250,11 @@ public static StopWatch createStarted() {
248250
*/
249251
private long stopTimeNanos;
250252

253+
/**
254+
* The list of splits
255+
*/
256+
private List<Split> splits;
257+
251258
/**
252259
* Constructs a new instance.
253260
*/
@@ -626,6 +633,59 @@ public void split() {
626633
splitState = SplitState.SPLIT;
627634
}
628635

636+
/**
637+
* <p>
638+
* Splits the time to track the elapsed time between two consecutive {@code split()} calls.
639+
* The label specified is used to identify each split
640+
* </p>
641+
*
642+
* <p>
643+
* After calling {@link #stop()}, we can call {@link #getReport()} to have a report with all time between each {@code split()} call, example:
644+
* </p>
645+
*
646+
* <pre>
647+
* 1 00:14:00.000
648+
* 2 00:02:00.000
649+
* 3 00:04:00.000
650+
* </pre>
651+
*
652+
* @param label A number to identify this split
653+
*
654+
* @throws IllegalStateException
655+
* if the StopWatch is not running.
656+
*/
657+
public void split(int label) {
658+
split(String.valueOf(label));
659+
}
660+
661+
/**
662+
* <p>
663+
* Splits the time to track the elapsed time between two consecutive {@code split()} calls.
664+
* The label specified is used to identify each split
665+
* </p>
666+
*
667+
* <p>
668+
* After calling {@link #stop()}, we can call {@link #getReport()} to have a report with all time between each {@code split()} call, example:
669+
* </p>
670+
*
671+
* <pre>
672+
* Baking cookies 00:14:00.000
673+
* Serving 00:02:00.000
674+
* Eating 00:04:00.000
675+
* </pre>
676+
*
677+
* @param label A message for string presentation.
678+
*
679+
* @throws IllegalStateException
680+
* if the StopWatch is not running.
681+
*/
682+
public void split(String label) {
683+
if (this.runningState != State.RUNNING) {
684+
throw new IllegalStateException("Stopwatch is not running.");
685+
}
686+
splits.add(new Split(label));
687+
}
688+
629689
/**
630690
* Starts this StopWatch.
631691
*
@@ -645,6 +705,7 @@ public void start() {
645705
startTimeNanos = System.nanoTime();
646706
startInstant = Instant.now();
647707
runningState = State.RUNNING;
708+
splits = new ArrayList<>();
648709
}
649710

650711
/**
@@ -674,10 +735,20 @@ public void stop() {
674735
if (runningState == State.RUNNING) {
675736
stopTimeNanos = System.nanoTime();
676737
stopInstant = Instant.now();
738+
split(StringUtils.EMPTY);
677739
}
678740
runningState = State.STOPPED;
679741
}
680742

743+
/**
744+
* Stops the watch if necessary
745+
*/
746+
private void stopIfNecessary() {
747+
if (this.runningState == State.RUNNING || this.runningState == State.SUSPENDED) {
748+
stop();
749+
}
750+
}
751+
681752
/**
682753
* Suspends this StopWatch for later resumption.
683754
*
@@ -746,4 +817,178 @@ public void unsplit() {
746817
splitState = SplitState.UNSPLIT;
747818
}
748819

820+
/**
821+
* Stops the watch and returns the list of splits with duration on each split (using milliseconds)
822+
* @return list of splits
823+
*/
824+
public List<Split> getProcessedSplits() {
825+
return getProcessedSplits(TimeUnit.MILLISECONDS);
826+
}
827+
828+
/**
829+
* Stops the watch and returns the list of splits with duration on each split (using nanoseconds)
830+
* @return list of splits
831+
*/
832+
public List<Split> getNanoProcessedSplits() {
833+
return getProcessedSplits(TimeUnit.NANOSECONDS);
834+
}
835+
836+
/**
837+
* Stops the watch and returns the list of splits with duration on each split
838+
*
839+
* @param timeUnit the unit of time, not null. Any value will calculate with milliseconds precision unless
840+
* {@code TimeUnit.NANOSECONDS} is specified.
841+
* @return list of splits
842+
*/
843+
public List<Split> getProcessedSplits(TimeUnit timeUnit) {
844+
stopIfNecessary();
845+
processSplits(timeUnit);
846+
final List<Split> result = new ArrayList<>(splits);
847+
848+
// we remove the last split because it's an internal and automatic split
849+
result.remove(result.size() - 1);
850+
851+
return result;
852+
}
853+
854+
/**
855+
* Fill durations (time took) on each split
856+
*
857+
* @param timeUnit the unit of time, not null. Any value will calculate with milliseconds precision unless
858+
* {@code TimeUnit.NANOSECONDS} is specified.
859+
*/
860+
private void processSplits(TimeUnit timeUnit) {
861+
// we need at least 2 splits to calculate the elapsed time
862+
if (splits.size() < 2) {
863+
return;
864+
}
865+
866+
for (int i = 0; i < splits.size() - 1; i++) {
867+
final Duration duration = Duration.between(splits.get(i).getStartTime(), splits.get(i+1).getStartTime());
868+
splits.get(i).setDuration(timeUnit == TimeUnit.NANOSECONDS ? duration.toNanos() : duration.toMillis());
869+
}
870+
871+
}
872+
873+
/**
874+
* <p>
875+
* Stops the watch and returns the splits report.
876+
* This report contains the elapsed time (on milliseconds) between each split
877+
* </p>
878+
*
879+
* @return the splits report
880+
*/
881+
public String getReport() {
882+
return getReport(TimeUnit.MILLISECONDS);
883+
}
884+
885+
/**
886+
* <p>
887+
* Stops the watch and returns the splits report.
888+
* This report contains the elapsed time (on nanoseconds) between each split
889+
* </p>
890+
*
891+
* @return the splits report
892+
*/
893+
public String getNanoReport() {
894+
return getReport(TimeUnit.NANOSECONDS);
895+
}
896+
897+
/**
898+
* <p>
899+
* Stops the watch and returns the splits report.
900+
* This report contains the elapsed time between each split
901+
* </p>
902+
*
903+
* @param timeUnit the unit of time, not null. Any value will calculate with milliseconds precision unless
904+
* {@code TimeUnit.NANOSECONDS} is specified.
905+
* @return the splits report
906+
*/
907+
private String getReport(TimeUnit timeUnit) {
908+
final StringBuilder report = new StringBuilder();
909+
910+
String duration;
911+
for (final Split split : getProcessedSplits(timeUnit)) {
912+
report.append(System.lineSeparator());
913+
report.append(split.getLabel()).append(StringUtils.SPACE);
914+
915+
if (timeUnit == TimeUnit.NANOSECONDS) {
916+
duration = String.valueOf(split.getDuration());
917+
} else {
918+
duration = DurationFormatUtils.formatDurationHMS(split.getDuration());
919+
}
920+
921+
report.append(duration);
922+
}
923+
924+
return report.toString();
925+
}
926+
927+
/**
928+
* Class to store details of each split
929+
*/
930+
protected static class Split {
931+
932+
/**
933+
* The start time of this split
934+
*/
935+
private Instant startTime = Instant.now();
936+
937+
/**
938+
* The duration (time took) on this split
939+
* This field is filled when user calls getSplits() or tries to print the splits report
940+
*/
941+
private long duration;
942+
943+
/*
944+
* The label for this split
945+
*/
946+
private String label;
947+
948+
/**
949+
* @param label Label for this split
950+
*/
951+
public Split(String label) {
952+
this.label = label;
953+
}
954+
955+
/**
956+
* <p>
957+
* Get the timestamp when this split was created
958+
* </p>
959+
*
960+
* @return startTime
961+
*/
962+
public Instant getStartTime() {
963+
return startTime;
964+
}
965+
966+
/**
967+
* <p>
968+
* Get the label of this split
969+
* </p>
970+
*
971+
* @return label
972+
*/
973+
public String getLabel() {
974+
return label;
975+
}
976+
977+
/**
978+
* Duration of this split
979+
* @return duration (time on ms or nano)
980+
*/
981+
public long getDuration() {
982+
return duration;
983+
}
984+
985+
/**
986+
* Set the duration of this split
987+
* @param duration time (on ms or nano)
988+
*/
989+
private void setDuration(long duration) {
990+
this.duration = duration;
991+
}
992+
}
993+
749994
}

0 commit comments

Comments
 (0)