Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for child ProgressBar #70

Open
wants to merge 2 commits into
base: 1.0
Choose a base branch
from
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
@@ -1,6 +1,7 @@
package me.tongfei.progressbar;

import java.io.PrintStream;
import java.util.List;

import static me.tongfei.progressbar.TerminalUtils.MOVE_CURSOR_TO_LINE_START;

Expand All @@ -26,8 +27,8 @@ public int getMaxProgressLength() {
}

@Override
public void accept(String str) {
out.print(MOVE_CURSOR_TO_LINE_START + str);
public void accept(List<String> str) {
out.print(MOVE_CURSOR_TO_LINE_START + str.get(0));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package me.tongfei.progressbar;

import java.text.DecimalFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.LinkedList;
import java.util.List;

/**
* Default progress bar renderer (see {@link ProgressBarRenderer}).
*
* @author Tongfei Chen
* @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 final ProgressBarStyle style;
private final String unitName;
private final long unitSize;
private final boolean isSpeedShown;
private final DecimalFormat speedFormat;

DefaultProgressBarRenderer(
ProgressBarStyle style,
Expand All @@ -32,56 +33,82 @@ public class DefaultProgressBarRenderer implements ProgressBarRenderer {
}

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

private int progressFractionalPart(ProgressState progress, int length) {
private int progressFractionalPart(ProgressStateImmutable progress, int length) {
double p = progress.getNormalizedProgress() * length;
double fraction = (p - Math.floor(p)) * style.fractionSymbols.length();
return (int) Math.floor(fraction);
}

private String eta(ProgressState progress, Duration elapsed) {
private String eta(ProgressStateImmutable progress) {
if (progress.max <= 0 || progress.indefinite) return "?";
else if (!progress.getChildren().isEmpty()) return Util.formatDuration(progress.eta());
else if (progress.current == 0) return "?";
else return Util.formatDuration(
elapsed.dividedBy(progress.current).multipliedBy(progress.max - progress.current)
);
else return Util.formatDuration(progress.eta());
}

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

private String ratio(ProgressState progress) {
private String ratio(ProgressStateImmutable progress) {
String m = progress.indefinite ? "?" : String.valueOf(progress.max / unitSize);
String c = String.valueOf(progress.current / unitSize);
return Util.repeat(' ', m.length() - c.length()) + c + "/" + m + unitName;
}

private String speed(ProgressState progress, Duration elapsed) {
if (elapsed.getSeconds() == 0) return "?" + unitName + "/s";
double speed = (double) progress.current / elapsed.getSeconds();
private String speed(ProgressStateImmutable progress) {
if (progress.elapsed.getSeconds() == 0) return "?" + unitName + "/s";
double speed = progress.speed();
double speedWithUnit = speed / unitSize;
return speedFormat.format(speedWithUnit) + unitName + "/s";
}

public String render(ProgressState progress, int maxLength) {
public List<String> render(ProgressState progress, int maxLength) {
return render(progress.getState(), maxLength, 0, false);
}

private List<String> render(ProgressStateImmutable progress, int maxLength, int subChild, boolean isLast) {
LinkedList<String> result = new LinkedList<>();
List<ProgressStateImmutable> children = progress.getChildren();
for (ProgressStateImmutable childState : children) {
List<String> render = render(childState, maxLength, subChild + 1, children.get(children.size() - 1) == childState);
result.addAll(render);
}

result.add(0, doRender(progress, maxLength, subChild, isLast));
return result;
}

private String doRender(ProgressStateImmutable progress, int maxLength, int subChild, boolean isLast) {

String prefix = "";
for (int i = 0; i < subChild; i++) {
if (i == subChild - 1) {
if (isLast) {
prefix += style.upRight;
} else {
prefix += style.verticalRight;
}
} else {
prefix += style.vertical;
}
}

Instant currTime = Instant.now();
Duration elapsed = Duration.between(progress.startTime, currTime);
prefix += progress.task + " " + percentage(progress) + " " + style.leftBracket;

String prefix = progress.task + " " + percentage(progress) + " " + style.leftBracket;
int maxSuffixLength = Math.max(maxLength - prefix.length(), 0);

String speedString = isSpeedShown ? speed(progress, elapsed) : "";
String speedString = isSpeedShown ? " " + speed(progress) : "";
String suffix = style.rightBracket + " " + ratio(progress) + " ("
+ Util.formatDuration(elapsed) + " / " + eta(progress, elapsed) + ") "
+ speedString + progress.extraMessage;
+ Util.formatDuration(progress.elapsed) + " / " + eta(progress) + ")"
+ speedString + (progress.extraMessage.isEmpty() ? "" : " " + progress.extraMessage);
// trim excessive suffix
if (suffix.length() > maxSuffixLength)
suffix = suffix.substring(0, maxSuffixLength);
Expand All @@ -93,7 +120,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 Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package me.tongfei.progressbar;

import java.util.List;
import java.util.function.Consumer;

/**
* Progress bar consumer that delegates the progress bar handling to a custom {@link Consumer}.
*
* @author Alex Peelman
* @since 0.8.0
*/
Expand All @@ -27,8 +29,8 @@ public int getMaxProgressLength() {
}

@Override
public void accept(String str) {
this.consumer.accept(str);
public void accept(List<String> strs) {
strs.forEach(this.consumer);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package me.tongfei.progressbar;

import java.io.PrintStream;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import static me.tongfei.progressbar.TerminalUtils.*;

Expand All @@ -13,22 +15,38 @@ public class InteractiveConsoleProgressBarConsumer extends ConsoleProgressBarCon

private boolean initialized = false;
int position = 1;
int childOffset = 1;

public InteractiveConsoleProgressBarConsumer(PrintStream out) {
super(out);
}

@Override
public void accept(String str) {
public void accept(List<String> str) {
if (!initialized) {
TerminalUtils.filterActiveConsumers(InteractiveConsoleProgressBarConsumer.class).forEach(c -> c.position++);
TerminalUtils.activeConsumers.add(this);
out.println(MOVE_CURSOR_TO_LINE_START + str);
out.println(MOVE_CURSOR_TO_LINE_START + str.get(0));
initialized = true;
return;
}
if (str.size() > childOffset) {
int offset = str.size() - childOffset;
childOffset += offset;
position += offset;
AtomicReference<Boolean> takeWhile = new AtomicReference<>(true);
TerminalUtils.filterActiveConsumers(InteractiveConsoleProgressBarConsumer.class).forEach(consumer -> {
if (consumer == this) takeWhile.set(false);
if (takeWhile.get()) consumer.position += offset;
});
for (int i = 0; i < offset; i++) {
out.println(MOVE_CURSOR_TO_LINE_START);
}
}

out.print(moveCursorUp(position) + str + moveCursorDown(position));
for (int i = str.size() - 1; i >= 0; i--) {
out.print(moveCursorUp(position - i) + str.get(i) + moveCursorDown(position - i));
}
}

@Override
Expand Down
43 changes: 19 additions & 24 deletions src/main/java/me/tongfei/progressbar/ProgressBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.time.Instant;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.concurrent.ExecutionException;
Expand All @@ -27,7 +26,7 @@
public class ProgressBar implements AutoCloseable {

private ProgressState progress;
private ProgressThread target;
private final ProgressThread target;
private ScheduledFuture scheduled;

/**
Expand Down Expand Up @@ -113,21 +112,13 @@ public ProgressBar(
ProgressBarConsumer consumer
) {
this.progress = new ProgressState(task, initialMax);
this.target = new ProgressThread(progress, renderer, updateIntervalMillis, consumer);
// starts the progress bar upon construction
progress.startTime = Instant.now();
this.target = new ProgressThread(this.progress, renderer, consumer);
scheduled = Util.executor.scheduleAtFixedRate(target, 0, updateIntervalMillis, TimeUnit.MILLISECONDS);
}

/**
* Starts this progress bar.
* @deprecated Please use the Java try-with-resource pattern instead.
*/
@Deprecated
public ProgressBar start() {
progress.startTime = Instant.now();
scheduled = Util.executor.scheduleAtFixedRate(target, 0, target.updateInterval, TimeUnit.MILLISECONDS);
return this;
private ProgressBar(ProgressState progress, ProgressThread target) {
this.progress = progress;
this.target = target;
}

/**
Expand Down Expand Up @@ -162,10 +153,10 @@ public ProgressBar step() {
*/
public ProgressBar maxHint(long n) {
if (n < 0)
progress.setAsIndefinite();
progress.setIndefinite(true);
else {
progress.setAsDefinite();
progress.maxHint(n);
progress.setIndefinite(false);
progress.setMax(n);
}
return this;
}
Expand All @@ -178,8 +169,7 @@ public ProgressBar maxHint(long n) {
public ProgressBar stop() {
try {
close();
}
catch (Exception e) { /* ignored, for backward compatibility */ }
} catch (Exception e) { /* ignored, for backward compatibility */ }
return this;
}

Expand Down Expand Up @@ -209,32 +199,32 @@ public ProgressBar setExtraMessage(String msg) {
return this;
}

/**
/**
* Returns the current progress.
*/
public long getCurrent() {
return progress.getCurrent();
return progress.getState().getCurrent();
}

/**
* Returns the maximum value of this progress bar.
*/
public long getMax() {
return progress.getMax();
return progress.getState().getMax();
}

/**
* Returns the name of this task.
*/
public String getTask() {
return progress.getTask();
return progress.getState().getTask();
}

/**
* Returns the extra message at the end of the progress bar.
*/
public String getExtraMessage() {
return progress.getExtraMessage();
return progress.getState().getExtraMessage();
}

// STATIC WRAPPER METHODS
Expand Down Expand Up @@ -353,4 +343,9 @@ public static <T, S extends BaseStream<T, S>> Stream<T> wrap(S stream, ProgressB
return StreamSupport.stream(sp, stream.isParallel());
}

public ProgressBar createChild(String task, long initialMax) {
ProgressState progress = new ProgressState(task, initialMax);
this.progress.addChild(progress);
return new ProgressBar(progress, target);
}
}
20 changes: 3 additions & 17 deletions src/main/java/me/tongfei/progressbar/ProgressBarConsumer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.tongfei.progressbar;

import java.util.List;
import java.util.function.Consumer;

/**
Expand All @@ -8,7 +9,7 @@
* @author Alex Peelman
* @author Tongfei Chen
*/
public interface ProgressBarConsumer extends Consumer<String>, Appendable, AutoCloseable {
public interface ProgressBarConsumer extends Consumer<List<String>>, AutoCloseable {

/**
* Returns the maximum length allowed for the rendered form of a progress bar.
Expand All @@ -19,22 +20,7 @@ public interface ProgressBarConsumer extends Consumer<String>, Appendable, AutoC
* Accepts a rendered form of a progress bar, e.g., prints to a specified stream.
* @param rendered Rendered form of a progress bar, a string
*/
void accept(String rendered);

default ProgressBarConsumer append(CharSequence csq) {
accept(csq.toString());
return this;
}

default ProgressBarConsumer append(CharSequence csq, int start, int end) {
accept(csq.subSequence(start, end).toString());
return this;
}

default ProgressBarConsumer append(char c) {
accept(String.valueOf(c));
return this;
}
void accept(List<String> rendered);

void close();

Expand Down
Loading