diff --git a/README.md b/README.md index e5906e6..be2b760 100644 --- a/README.md +++ b/README.md @@ -2,34 +2,34 @@ This is a simple library for adding one or multiple console progress counter(s) to your project. +![demo.png](demo.png) ### I want to use and/or adapt this project - Go for it - I tried to make everything as readable and modifiable as possible. Check out the explanation below, and the license in the 'LICENSE' file. Regardless of the license, it would be cool if you somehow mentioned, that you got this code from here :) ### How to use - 1) Implement the [Trackable.java](https://github.com/danielbinder/Progress/blob/master/src/Trackable.java) in the Objects you want to track. - 2) Wherever you want to track progress, wrap the Object to track with - - `Progress.of(Object)` - - `Progress.of(Description, Object)` - - `Progress.of(Object1, Object2, ...)` - - `Progress.of(List)` - - `Progress.of(List, List)` - - `Progress.of(Map)` - 3) All `Progress.of(...)` methods in [Progress.java](https://github.com/danielbinder/Progress/blob/master/src/Progress.java) return the incoming objects back to you. - If a single object was passed, it is returned directly. - Multiple objects are returned as a List. +1) Add as a library (here shown in IntelliJ) or add the code directly to your project + ![lib.png](lib.png) +2) Wrap the Object you want to track in `Progresss.of("optional description", someObj)` wherever you want to consume it e.g. + - You can use it anywhere, where you would use an iterator of a Collection or an Array, since it literally returns one + - If you don't add a description, an incrementing counter will be added instead + - ``` + for(Thing t : Progress.of(thingList)) { + doSomethingWith(t); + } + ``` +3) You can also register multiple Collections in multiple Threads +4) If you want to reset the counter use `Progress.reset()` +5) You can find more details in the [Demo.java](https://github.com/danielbinder/Progress/blob/master/src/Demo.java) ### Implementation details - - Update rate of the counter is hard coded to 100ms. - - The goal of complicated progress update loop is to reduce IO access as much as possible. - - The progress will be shown as a percentage (int) - - Time is `elapsed time | estimated time left` +- Update rate of the counter is hard coded to 100ms. +- The goal of complicated progress update loop is to reduce IO access as much as possible. +- The progress will be shown as a percentage (int) +- Time is `elapsed time | estimated time left` ### Demo The [Demo.java](https://github.com/danielbinder/Progress/blob/master/src/Demo.java) demonstrates some usages described above, where the progress is determined by counters that increase randomly. -Here you can see how it should look in your console: - -![demo.png](demo.png) +Here you can see how it should look in your console above. diff --git a/demo.png b/demo.png index 99a5e64..c043c3c 100644 Binary files a/demo.png and b/demo.png differ diff --git a/lib.png b/lib.png new file mode 100644 index 0000000..ea89e0e Binary files /dev/null and b/lib.png differ diff --git a/src/Demo.java b/src/Demo.java index 2337773..766cd56 100644 --- a/src/Demo.java +++ b/src/Demo.java @@ -1,45 +1,41 @@ +import java.util.List; import java.util.Random; +import java.util.stream.IntStream; -public class Demo implements Trackable { // Demo needs to be Trackable! - private int i = 0; - +public class Demo { public void count() { Random r = new Random(); - // Simulate real progress by increasing i - for(i = 0; i < 100; i++) { - try { - // Sleep up to 1s - Thread.sleep(r.nextInt(1, 1000)); - } catch(InterruptedException ignored) {} - } - } - - @Override - public int currentProgress() { - // In a real example 'i' would be calculated in some way e.g. (int) ((filesCopied / allFiles) * 100) - return i; + // Simulate real progress + try { + // Sleep up to 1s + Thread.sleep(r.nextInt(1, 1000)); + } catch(InterruptedException ignored) {} } public static void main(String[] args) { System.out.println("Single counter demo:"); - Progress.of(new Demo()) // Returns the Demo object passed in - .count(); + List demos = IntStream.range(0, 100) + .mapToObj(i -> new Demo()) + .toList(); + + for(Demo d : Progress.of(demos)) d.count(); + // My console won't update to 100% unless I add this try { - // Without this, my console doesn't see the need to update to 100% Thread.sleep(100); } catch(InterruptedException ignored) {} System.out.println("\n\nMulticounter demo:"); - Progress.of(new Demo(), - new Demo(), - new Demo(), - new Demo(), - new Demo()) - // Returns List objects passed in - .stream() + Progress.reset(); + IntStream.range(0, 5) + .mapToObj(l -> IntStream.range(0, 100) + .mapToObj(i -> new Demo()) + .toList()) .parallel() - .forEach(Demo::count); + // Register multiple lists + .forEach(list -> { + for(Demo d : Progress.of(list)) d.count(); + }); } } diff --git a/src/Progress.java b/src/Progress.java index ef79301..18fb5c5 100644 --- a/src/Progress.java +++ b/src/Progress.java @@ -1,105 +1,100 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; -public class Progress { - public static T of(T trackable) { - return of("", trackable); - } +public class Progress implements Iterable { + private static final Map progressMap = new HashMap<>(); + private static int i = 0; + private static long startTime; + private final String description; + private final List list; - public static T of(String description, T trackable) { - new Thread() { - @Override - public void run() { - super.run(); - - int currentProgress = trackable.currentProgress(); - String s = ""; - - long startTime = System.currentTimeMillis(); - - while(currentProgress < 100) { - int currentProgressTemp = trackable.currentProgress(); + static { + reset(); + } - if(currentProgress != currentProgressTemp) { - System.out.print("\b".repeat(s.length())); + private Progress(String description, List list) { + progressMap.put(description, 0); - currentProgress = currentProgressTemp; - s = "\u001B[32mProgress" + (description.isBlank() ? "" : "[" + description + "]") + ": " + - currentProgress + "% " + getTimeString(startTime, currentProgress) + - "\u001B[0m"; + this.description = description; + this.list = list; + } - System.out.print(s); - } + public static Progress of(String description, List list) { + return new Progress<>(description, list); + } - try { - Thread.sleep(100); - } catch(InterruptedException ignored) {} - } - } - }.start(); + public static Progress of(List list) { + return of(String.valueOf(i++), list); + } - return trackable; + public static Progress of(String description, Collection collection) { + return of(description, collection.stream().toList()); } - /** Multiprogress */ + public static Progress of(Collection collection) { + return of(collection.stream().toList()); + } - @SafeVarargs - public static List of(T...trackables) { - return of(Arrays.stream(trackables).toList()); + public static Progress of(String description, T...array) { + return of(description, Arrays.asList(array)); } - public static List of(List trackables) { - return of(trackables.stream() - .map(t -> String.valueOf(trackables.indexOf(t))) - .toList(), - trackables); + public static Progress of(T...array) { + return of(Arrays.asList(array)); } - public static List of(Map trackablesWithDescriptions) { - List descriptions = new ArrayList<>(); - List trackables = new ArrayList<>(); + @Override + public Iterator iterator() { + return new Iterator<>() { + private int i = 0; - trackablesWithDescriptions.forEach((k, v) -> { - trackables.add(k); - descriptions.add(v); - }); + @Override + public boolean hasNext() { + return i < list.size(); + } - return of(descriptions, trackables); + @Override + public T next() { + updateProgress(description, (100 * i) / Math.max(1, list.size() - 1)); + + return list.get(i++); + } + }; } - public static List of(List descriptions, List trackables) { - List transformedDescriptions = descriptions.stream() - .map(d -> "Progress[" + d + "]: ") - .toList(); + private static void updateProgress(String description, int percentage) { + progressMap.put(description, percentage); + } + public static void reset() { + progressMap.clear(); + i = 0; + startTime = System.currentTimeMillis(); new Thread() { @Override public void run() { super.run(); + while(progressMap.isEmpty()) { + try { + Thread.sleep(100); + } catch(InterruptedException ignored) {} + } + String oldString = ""; - int minProgress = trackables.stream() - .map(Trackable::currentProgress) + int minProgress = progressMap.values().stream() .min(Integer::compareTo) // stop if no minimum found .orElse(100); - long startTime = System.currentTimeMillis(); - while(minProgress < 100) { - minProgress = trackables.stream() - .map(Trackable::currentProgress) + minProgress = progressMap.values().stream() .min(Integer::compareTo) // stop if no minimum found .orElse(100); - - - String currString = "\u001B[32m" + transformedDescriptions.stream() - .map(d -> d + trackables.get(transformedDescriptions.indexOf(d)).currentProgress() + "% ") + String currString = "\u001B[32m" + progressMap.keySet().stream() + .map(d -> "[" + d + "] " + progressMap.get(d) + "% ") .collect(Collectors.joining("", "", getTimeString(startTime, minProgress))); if(!currString.equals(oldString)) { @@ -116,8 +111,6 @@ public void run() { } } }.start(); - - return trackables; } private static String getTimeString(long startTime, int minProgress) { diff --git a/src/Trackable.java b/src/Trackable.java deleted file mode 100644 index d8eb5e5..0000000 --- a/src/Trackable.java +++ /dev/null @@ -1,6 +0,0 @@ -public interface Trackable { - /** - * @return the Current Progress in percent including both 0 and 100 - */ - int currentProgress(); -}