Skip to content

Commit 9dff16e

Browse files
feat(action, PVQ-5008): Added progress bar to java app that runs in separate thread. Also rearrange classes so public functions are first and then private.
1 parent 69ed6e7 commit 9dff16e

3 files changed

Lines changed: 319 additions & 130 deletions

File tree

src/main/java/net/pdfix/App.java

Lines changed: 66 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.io.File;
44
import java.io.FileInputStream;
5-
import java.io.FileNotFoundException;
65
import java.io.IOException;
76
import java.nio.file.Files;
87
import java.nio.file.Paths;
@@ -15,18 +14,8 @@
1514
public class App {
1615
private static String VERSION = "1.0.0";
1716
private static String APP_NAME = "Validate PDF Accessibility";
17+
private static String OP_DUPLICATE_MCID = "OP_DUPLICATE_MCID";
1818

19-
private static void displayVersion() {
20-
Properties properties = new Properties();
21-
try {
22-
properties.load(App.class.getClassLoader().getResourceAsStream("version.properties"));
23-
VERSION = properties.getProperty("project.version");
24-
APP_NAME = properties.getProperty("project.name");
25-
System.out.println(APP_NAME + " v" + VERSION + "\n");
26-
} catch (IOException e) {
27-
System.err.println("Error reading version information.");
28-
}
29-
}
3019

3120
public static boolean isPDFFile(String filePath) {
3221
File file = new File(filePath);
@@ -53,39 +42,6 @@ public static boolean isPDFFile(String filePath) {
5342
}
5443
}
5544

56-
// Collects all files from the specified directory
57-
private static List<File> collectFiles(String directoryPath) {
58-
List<File> fileList = new ArrayList<>();
59-
try {
60-
Files.walk(Paths.get(directoryPath))
61-
.filter(Files::isRegularFile) // Include only regular files (not directories)
62-
.forEach(path -> fileList.add(path.toFile()));
63-
} catch (IOException e) {
64-
System.err.println("Error reading files: " + e.getMessage());
65-
}
66-
return fileList;
67-
}
68-
69-
private static int processFile(File file) throws Exception {
70-
// Process single file
71-
System.out.println("File: " + file.getPath() + "");
72-
73-
if (!isPDFFile(file.getAbsolutePath())) {
74-
System.out.println("Not a PDF file");
75-
return 0;
76-
}
77-
78-
int count = FindDuplicateMcid.checkDuplicateMcid(file.getAbsolutePath());
79-
if (count == 0) {
80-
System.out.println("No duplicate MCIDs found");
81-
} else {
82-
System.out.println(String.format("Total %d duplicate MCID(s) found", count));
83-
}
84-
return count;
85-
}
86-
87-
private static String OP_DUPLICATE_MCID = "OP_DUPLICATE_MCID";
88-
8945
public static void main(String[] args) throws Exception {
9046
displayVersion();
9147

@@ -94,7 +50,11 @@ public static void main(String[] args) throws Exception {
9450
String last = "";
9551

9652
String op = "";
53+
54+
ConsoleProgressBar progressBar = null;
55+
9756
try {
57+
// Parse arguments
9858
for (String s : args) {
9959
if (last.isEmpty()) {
10060
if (s.equals("--help")) {
@@ -132,6 +92,8 @@ public static void main(String[] args) throws Exception {
13292
throw new RuntimeException("Missing operation argument. See --help");
13393
}
13494

95+
progressBar = new ConsoleProgressBar("Gathering files");
96+
13597
List<File> fileList = new ArrayList<>();
13698
if (!inputFile.isEmpty()) {
13799
fileList.add(new File(inputFile));
@@ -154,6 +116,10 @@ public int compare(File f1, File f2) {
154116
}
155117
});
156118

119+
progressBar.update(10f);
120+
progressBar.setTitle("Processing files");
121+
float step = fileList.size() / 90f;
122+
157123
int count = 0;
158124

159125
// Process each file
@@ -167,13 +133,68 @@ public int compare(File f1, File f2) {
167133
System.err.println(e.getLocalizedMessage());
168134
}
169135
System.out.println("===============================================================================\n");
136+
progressBar.update(step);
170137
}
171138
System.out.println("Process complete");
139+
140+
progressBar.setProgress(100f);
141+
progressBar.setTitle("Done");
142+
progressBar.waitUntilFinished();
143+
172144
System.exit(Math.min(count, ExitCodes.MAX_SUCCESS));
173145
} catch (Exception e) {
146+
if ((progressBar != null) && progressBar.isRunning()) {
147+
progressBar.stop();
148+
}
149+
174150
System.err.println(ExitCodes.GENERAL_MESSAGE);
175151
System.err.println(e.getLocalizedMessage());
176152
System.exit(ExitCodes.GENERAL_ERROR);
177153
}
154+
155+
}
156+
157+
158+
private static void displayVersion() {
159+
Properties properties = new Properties();
160+
try {
161+
properties.load(App.class.getClassLoader().getResourceAsStream("version.properties"));
162+
VERSION = properties.getProperty("project.version");
163+
APP_NAME = properties.getProperty("project.name");
164+
System.out.println(APP_NAME + " v" + VERSION + "\n");
165+
} catch (IOException e) {
166+
System.err.println("Error reading version information.");
167+
}
168+
}
169+
170+
// Collects all files from the specified directory
171+
private static List<File> collectFiles(String directoryPath) {
172+
List<File> fileList = new ArrayList<>();
173+
try {
174+
Files.walk(Paths.get(directoryPath))
175+
.filter(Files::isRegularFile) // Include only regular files (not directories)
176+
.forEach(path -> fileList.add(path.toFile()));
177+
} catch (IOException e) {
178+
System.err.println("Error reading files: " + e.getMessage());
179+
}
180+
return fileList;
181+
}
182+
183+
private static int processFile(File file) throws Exception {
184+
// Process single file
185+
System.out.println("File: " + file.getPath() + "");
186+
187+
if (!isPDFFile(file.getAbsolutePath())) {
188+
System.out.println("Not a PDF file");
189+
return 0;
190+
}
191+
192+
int count = FindDuplicateMcid.checkDuplicateMcid(file.getAbsolutePath());
193+
if (count == 0) {
194+
System.out.println("No duplicate MCIDs found");
195+
} else {
196+
System.out.println(String.format("Total %d duplicate MCID(s) found", count));
197+
}
198+
return count;
178199
}
179200
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package net.pdfix;
2+
3+
import java.io.PrintStream;
4+
5+
public class ConsoleProgressBar implements Runnable {
6+
7+
private boolean firstOutput = false;
8+
private final int outputLength = 60;
9+
private final int percentStart = 25;
10+
private volatile float progress;
11+
private final int totalUnits;
12+
private volatile String title;
13+
14+
private final PrintStream out;
15+
16+
private final long startTime;
17+
18+
private final Thread thread;
19+
private volatile boolean running = true;
20+
private volatile boolean stopped = false;
21+
22+
private final Object wakeLock = new Object();
23+
24+
private static final int BAR_WIDTH = 20;
25+
private static final long UPDATE_INTERVAL_MS = 250; // ~4 updates/sec
26+
27+
28+
// Constructors
29+
public ConsoleProgressBar() {
30+
this("Progress", 100, true);
31+
}
32+
33+
public ConsoleProgressBar(String title) {
34+
this(title, 100, true);
35+
}
36+
37+
public ConsoleProgressBar(String title, int totalUnits) {
38+
this(title, totalUnits, true);
39+
}
40+
41+
public ConsoleProgressBar(String title, int totalUnits, boolean useErrorStream) {
42+
this.title = title;
43+
this.totalUnits = totalUnits;
44+
this.progress = 0;
45+
this.startTime = System.currentTimeMillis();
46+
47+
this.out = useErrorStream ? System.err : System.out;
48+
49+
thread = new Thread(this, "ConsoleProgressBar");
50+
thread.setDaemon(true);
51+
thread.start();
52+
}
53+
54+
// Check if progress bar is running
55+
public boolean isRunning() {
56+
return running;
57+
}
58+
59+
// Check if progress bar thread ended
60+
public boolean isStopped() {
61+
return stopped;
62+
}
63+
64+
@Override
65+
public void run() {
66+
while (running) {
67+
68+
printProgress(firstOutput, false);
69+
firstOutput = false;
70+
71+
if (progress >= totalUnits) {
72+
running = false;
73+
break;
74+
}
75+
76+
try {
77+
synchronized (wakeLock) {
78+
wakeLock.wait(UPDATE_INTERVAL_MS);
79+
}
80+
} catch (InterruptedException ignored) {}
81+
}
82+
83+
printProgress(firstOutput, true);
84+
stopped = true;
85+
}
86+
87+
// Set absolute progress
88+
public void setProgress(float value) {
89+
progress = value;
90+
wake();
91+
}
92+
93+
// Change title
94+
public void setTitle(String newTitle) {
95+
if (newTitle.length() > (percentStart - 3)) {
96+
System.err.println("Title for progress bar that you have chosen is longer than allowed, cutting.");
97+
newTitle = newTitle.substring(0, percentStart - 3);
98+
}
99+
title = newTitle;
100+
wake();
101+
}
102+
103+
// Stop thread on background
104+
public void stop() throws InterruptedException {
105+
running = false;
106+
wake();
107+
waitUntilFinished();
108+
}
109+
110+
// Increment progress
111+
public void update(float delta) {
112+
progress += delta;
113+
wake();
114+
}
115+
116+
// Wait till thread finishes
117+
public void waitUntilFinished() throws InterruptedException {
118+
thread.join();
119+
}
120+
121+
122+
private void printProgress(boolean isFirst, boolean isLast) {
123+
float percent = Math.min(progress / totalUnits, 1.0f);
124+
int percentInt = (int) (percent * 100);
125+
int filled = (int) (percent * BAR_WIDTH);
126+
long elapsed = (System.currentTimeMillis() - startTime) / 1000;
127+
128+
//Build the output string
129+
StringBuilder line = new StringBuilder(80);
130+
131+
if (!isFirst) {
132+
line.append("\r");
133+
}
134+
135+
line.append(String.format("%s :", title));
136+
137+
for (int i = line.length(); i < percentStart; i++) {
138+
line.append(' ');
139+
}
140+
141+
line.append(String.format("%3d%% [", percentInt));
142+
143+
for (int i = 0; i < BAR_WIDTH; i++) {
144+
line.append(i < filled ? '█' : '-');
145+
}
146+
147+
line.append(String.format("] %ds", elapsed));
148+
149+
int padding = Math.max(0, outputLength - line.length());
150+
if (padding > 0) {
151+
line.append(" ".repeat(padding));
152+
}
153+
154+
if (isLast) {
155+
line.append("\n");
156+
}
157+
158+
//print it
159+
out.print(line.toString());
160+
}
161+
162+
private void wake() {
163+
synchronized (wakeLock) {
164+
wakeLock.notifyAll();
165+
}
166+
}
167+
}

0 commit comments

Comments
 (0)