Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,39 @@

import java.text.DecimalFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
import java.util.function.Function;

import static me.tongfei.progressbar.StringDisplayUtils.*;
import static me.tongfei.progressbar.StringDisplayUtils.getStringDisplayLength;
import static me.tongfei.progressbar.StringDisplayUtils.trimDisplayLength;

/**
* Default progress bar renderer (see {@link ProgressBarRenderer}).
*
* @author Tongfei Chen
* @author Muhammet Sakarya
* @since 0.8.0
*/
public class DefaultProgressBarRenderer implements ProgressBarRenderer {

private ProgressBarStyle style;
private String unitName;
private long unitSize;
private boolean isSpeedShown;
private DecimalFormat speedFormat;
private ChronoUnit speedUnit;
private boolean isEtaShown;
private Function<ProgressState, Optional<Duration>> eta;
private final ProgressBarStyle style;

private final String unitName;

private final long unitSize;

private final boolean isSpeedShown;

private final TimeFormat timeFormat;

private final DecimalFormat speedFormat;

private final ChronoUnit speedUnit;

private final boolean isEtaShown;

private final Function<ProgressState, Optional<Duration>> eta;

protected DefaultProgressBarRenderer(
ProgressBarStyle style,
Expand All @@ -34,12 +44,14 @@ protected DefaultProgressBarRenderer(
DecimalFormat speedFormat,
ChronoUnit speedUnit,
boolean isEtaShown,
Function<ProgressState, Optional<Duration>> eta
Function<ProgressState, Optional<Duration>> eta,
TimeFormat timeFormat
) {
this.style = style;
this.unitName = unitName;
this.unitSize = unitSize;
this.isSpeedShown = isSpeedShown;
this.timeFormat = timeFormat;
this.speedFormat = isSpeedShown && speedFormat == null ? new DecimalFormat() : speedFormat;
this.speedUnit = speedUnit;
this.isEtaShown = isEtaShown;
Expand All @@ -48,7 +60,7 @@ protected DefaultProgressBarRenderer(

// Number of full blocks
protected int progressIntegralPart(ProgressState progress, int length) {
return (int)(progress.getNormalizedProgress() * length);
return (int) (progress.getNormalizedProgress() * length);
}

protected int progressFractionalPart(ProgressState progress, int length) {
Expand All @@ -58,19 +70,21 @@ protected int progressFractionalPart(ProgressState progress, int length) {
}

protected String etaString(ProgressState progress) {
Optional<Duration> eta = this.eta.apply(progress);
if (eta.isPresent()) {
return Util.formatDuration(eta.get());
}
else {
final Optional<Duration> optEta = this.eta.apply(progress);
if (optEta.isPresent()) {
return DurationFormatter.formatDuration(optEta.get(), timeFormat);
} else {
return "?";
}
}

protected String percentage(ProgressState progress) {
String res;
if (progress.max <= 0 || progress.indefinite) res = "? %";
else res = String.valueOf((int) Math.floor(100.0 * progress.current / progress.max)) + "%";
if (progress.max <= 0 || progress.indefinite) {
res = "? %";
} else {
res = String.valueOf((int) Math.floor(100.0 * progress.current / progress.max)) + "%";
}
return Util.repeat(' ', 4 - res.length()) + res;
}

Expand All @@ -84,7 +98,7 @@ protected String speed(ProgressState progress) {
String suffix = "/s";
double elapsedSeconds = progress.getElapsedAfterStart().getSeconds();
double elapsedInUnit = elapsedSeconds;
if (null != speedUnit)
if (null != speedUnit) {
switch (speedUnit) {
case MINUTES:
suffix = "/min";
Expand All @@ -99,9 +113,11 @@ protected String speed(ProgressState progress) {
elapsedInUnit /= (60 * 60 * 24);
break;
}
}

if (elapsedSeconds == 0)
if (elapsedSeconds == 0) {
return "?" + unitName + suffix;
}
double speed = (double) (progress.current - progress.start) / elapsedInUnit;
double speedWithUnit = speed / unitSize;
return speedFormat.format(speedWithUnit) + unitName + suffix;
Expand All @@ -125,7 +141,7 @@ public String render(ProgressState progress, int maxLength) {

String speedString = isSpeedShown ? speed(progress) : "";
String suffix = style.rightBracket + " " + ratio(progress) + " ("
+ Util.formatDuration(progress.getTotalElapsed())
+ DurationFormatter.formatDuration(progress.getTotalElapsed(), timeFormat)
+ (isEtaShown ? " / " + etaString(progress) : "")
+ ") "
+ speedString + progress.extraMessage;
Expand All @@ -143,7 +159,7 @@ public String render(ProgressState progress, int maxLength) {

// case of indefinite progress bars
if (progress.indefinite) {
int pos = (int)(progress.current % length);
int pos = (int) (progress.current % length);
sb.append(Util.repeat(style.space, pos));
sb.append(style.block);
sb.append(Util.repeat(style.space, length - pos - 1));
Expand All @@ -156,8 +172,7 @@ public String render(ProgressState progress, int maxLength) {
if (fraction != 0) {
sb.append(style.fractionSymbols.charAt(fraction));
sb.append(style.delimitingSequence);
}
else {
} else {
sb.append(style.delimitingSequence);
sb.append(style.rightSideFractionSymbol);
}
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/me/tongfei/progressbar/DurationFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package me.tongfei.progressbar;

import java.time.Duration;

public class DurationFormatter {

private DurationFormatter() {
}

public static String formatDuration(Duration d) {
return formatDuration(d, TimeFormat.DEFAULT);
}

public static String formatDuration(Duration d, TimeFormat format) {
final long s = d.getSeconds();
switch (format) {
case SECONDS:
return formatAsSeconds(s);
case MINUTES_SECONDS:
return formatAsMinutesAndSeconds(s);
case ADAPTED: {
if (s < 60) {
return formatAsSeconds(s);
} else if (s < 3600) {
return formatAsMinutesAndSeconds(s);
} else {
return formatAsHoursMinutedAndSeconds(s);
}
}
case DEFAULT:
default:
return formatAsHoursMinutedAndSeconds(s);
}
}

private static String formatAsSeconds(long s) {
return String.format("%d", s);
}

private static String formatAsMinutesAndSeconds(long s) {
return String.format("%d:%02d", s / 60, s % 60);
}

private static String formatAsHoursMinutedAndSeconds(long s) {
return String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, s % 60);
}

}
8 changes: 5 additions & 3 deletions src/main/java/me/tongfei/progressbar/ProgressBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ public ProgressBar(
long processed,
Duration elapsed
) {
this(task, initialMax, updateIntervalMillis, continuousUpdate, clearDisplayOnFinish, processed, elapsed,
this(task, null, initialMax, updateIntervalMillis, continuousUpdate, clearDisplayOnFinish, processed, elapsed,
new DefaultProgressBarRenderer(
style, unitName, unitSize,
showSpeed, speedFormat, speedUnit,
true, Util::linearEta
true, Util::linearEta, TimeFormat.DEFAULT
),
createConsoleConsumer(os)
);
Expand All @@ -85,6 +85,7 @@ public ProgressBar(
* Creates a progress bar with the specific name, initial maximum value, customized update interval (default 1s),
* and the provided progress bar renderer ({@link ProgressBarRenderer}) and consumer ({@link ProgressBarConsumer}).
* @param task Task name
* @param taskFixedLength Fixed length of task name (null equals no fixed length)
* @param initialMax Initial maximum value
* @param updateIntervalMillis Update time interval (default value 1000ms)
* @param continuousUpdate Rerender every time the update interval happens regardless of progress count.
Expand All @@ -96,6 +97,7 @@ public ProgressBar(
*/
public ProgressBar(
String task,
Integer taskFixedLength,
long initialMax,
int updateIntervalMillis,
boolean continuousUpdate,
Expand All @@ -105,7 +107,7 @@ public ProgressBar(
ProgressBarRenderer renderer,
ProgressBarConsumer consumer
) {
this.progress = new ProgressState(task, initialMax, processed, elapsed);
this.progress = new ProgressState(task, taskFixedLength, initialMax, processed, elapsed);
this.action = new ProgressUpdateAction(progress, renderer, consumer, continuousUpdate, clearDisplayOnFinish);
scheduledTask = Util.executor.scheduleAtFixedRate(
action, 0, updateIntervalMillis, TimeUnit.MILLISECONDS
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/me/tongfei/progressbar/ProgressBarBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
public class ProgressBarBuilder {

private String task = "";
private Integer taskFixedLength = null;
private long initialMax = -1;
private int updateIntervalMillis = 1000;
private boolean continuousUpdate = false;
Expand All @@ -22,6 +23,7 @@ public class ProgressBarBuilder {
private boolean clearDisplayOnFinish = false;
private String unitName = "";
private long unitSize = 1;
private TimeFormat timeFormat = TimeFormat.DEFAULT;
private boolean showSpeed = false;
private boolean hideEta = false;
private Function<ProgressState, Optional<Duration>> eta = Util::linearEta;
Expand All @@ -40,6 +42,11 @@ public ProgressBarBuilder setTaskName(String task) {
return this;
}

public ProgressBarBuilder setTaskFixedLength(Integer fixedLength) {
this.taskFixedLength = fixedLength;
return this;
}

boolean initialMaxIsSet() {
return this.initialMax != -1;
}
Expand Down Expand Up @@ -90,6 +97,11 @@ public ProgressBarBuilder setRenderer(ProgressBarRenderer renderer) {
return this;
}

public ProgressBarBuilder setTimeFormat(TimeFormat timeFormat) {
this.timeFormat = timeFormat;
return this;
}

public ProgressBarBuilder showSpeed() {
return showSpeed(new DecimalFormat("#.0"));
}
Expand Down Expand Up @@ -130,6 +142,7 @@ public ProgressBarBuilder startsFrom(long processed, Duration elapsed) {
public ProgressBar build() {
return new ProgressBar(
task,
taskFixedLength,
initialMax,
updateIntervalMillis,
continuousUpdate,
Expand All @@ -138,9 +151,9 @@ public ProgressBar build() {
elapsed,
(renderer == null
? new DefaultProgressBarRenderer(
style, unitName, unitSize,
style, unitName, unitSize,
showSpeed, speedFormat, speedUnit,
!hideEta, eta)
!hideEta, eta, timeFormat)
: renderer
),
(consumer == null
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/me/tongfei/progressbar/ProgressState.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
public class ProgressState {

String taskName;

private final Integer taskFixedLength;

String extraMessage = "";

boolean indefinite = false;

// 0 start current max
// [===============|=========> ]
long start;
long current;
long max;
Expand All @@ -27,8 +28,9 @@ public class ProgressState {
volatile boolean alive = true;
volatile boolean paused = false;

ProgressState(String taskName, long initialMax, long startFrom, Duration elapsedBeforeStart) {
ProgressState(String taskName, Integer taskFixedLength, long initialMax, long startFrom, Duration elapsedBeforeStart) {
this.taskName = taskName;
this.taskFixedLength = taskFixedLength;
if (initialMax < 0)
indefinite = true;
else this.max = initialMax;
Expand All @@ -40,9 +42,18 @@ public class ProgressState {
}

public String getTaskName() {
if (taskFixedLength != null) {
return getPaddedTaskName(taskName, taskFixedLength);
}
return taskName;
}

private String getPaddedTaskName(String task, int length) {
final String str = task.length() < length ? task : task.substring(0, length);
final String format = "%-" + length + "s";
return String.format(format, str);
}

public synchronized String getExtraMessage() {
return extraMessage;
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/me/tongfei/progressbar/TimeFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package me.tongfei.progressbar;

public enum TimeFormat {
DEFAULT,
ADAPTED,
SECONDS,
MINUTES_SECONDS
}
5 changes: 0 additions & 5 deletions src/main/java/me/tongfei/progressbar/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ static String repeat(char c, int n) {
return new String(s);
}

static String formatDuration(Duration d) {
long s = d.getSeconds();
return String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, s % 60);
}

static Optional<Duration> linearEta(ProgressState progress) {
if (progress.getMax() <= 0 || progress.isIndefinite()) return Optional.empty();
else if (progress.getCurrent() - progress.getStart() == 0) return Optional.empty();
Expand Down
Loading