Skip to content

Suggestion for Interface where "ProgressBar" can pick up the progress state. #172

@pfeiferd

Description

@pfeiferd

At the moment the design for the ProgressBar-Library is such that the ProgressState must actively be changed by calling library code - either implicitly via some wrapper or by using stepBy(), stepTo() etc.
However, in some cases it might be more performant and easier if the ProgressBar would pick up the changing progress state right before rendering from some source that must implement a corresponding interface like:

public interface GSProgressUpdate {
    public long current();
    public long max();
}

In some cases this is more efficient than actively updating the progress state via stepBy(), stepTo(), since GSProgressUpdate would only be consulted right before rendering. I was able to implement this as a sort of "workaround" without changing existing library code. But it would be nicer if such an approach was already integrated in the library as an option. Here is a renderer that uses the above give interface:

private static class GSProgressBarRenderer extends DefaultProgressBarRenderer {
        private final GSProgressUpdate progressUpdate;
        private ProgressBar progressBar;

        protected GSProgressBarRenderer(String unitName, GSProgressUpdate progressUpdateProvider) {
            super(ProgressBarStyle.ASCII, unitName, 1, false, null, null, true, GSProgressBarRenderer::linearEta);
            this.progressUpdate = progressUpdateProvider;
        }

        public void setProgressBar(ProgressBar progressBar) {
            this.progressBar = progressBar;
        }

        @Override
        public String render(ProgressState progress, int maxLength) {
            if (progressUpdate != null) {
                progressBar.maxHint(progressUpdate.max());
                progressBar.stepTo(progressUpdate.current());
            }
            return super.render(progress, maxLength);
        }

        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();
            else return Optional.of(
                        progress.getElapsedAfterStart()
                                .dividedBy(progress.getCurrent() - progress.getStart())
                                .multipliedBy(progress.getMax() - progress.getCurrent())
                );
        }
    }

Creating a matching progress bar in the context of this workaround as follows:

public static ProgressBar newGSProgressBar(String task, int updateIntervalMillis, String unitName, GSProgressUpdate progressUpdate, Log log) {
        GSProgressBarRenderer renderer = new GSProgressBarRenderer(unitName, progressUpdate);
        ProgressBarBuilder progressBarBuilder = new ProgressBarBuilder().
                setTaskName(task).setUpdateIntervalMillis(updateIntervalMillis).
                setUnit(unitName, 1).setRenderer(renderer).setMaxRenderedLength(100).continuousUpdate();
        if (log != null && log.isInfoEnabled()) {
            progressBarBuilder.setConsumer(new DelegatingProgressBarConsumer(log::info));
        }
        ProgressBar progressBar = progressBarBuilder.build();
        renderer.setProgressBar(progressBar);
        return progressBar;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions