From db4a75fd130ec2077084f0a3345e9160c32ab188 Mon Sep 17 00:00:00 2001 From: Mattias Nordahl Date: Sat, 13 Jul 2024 20:54:20 +0200 Subject: [PATCH] Fixed issue where resources did not load when running JAR --- .gitignore | 3 + build.gradle | 33 +++++- src/main/java/util/ExamplesHandler.java | 133 ++++++++++++++++-------- src/main/java/view/HelpWindow.java | 13 +-- todo.txt | 2 + 5 files changed, 127 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index 5846009..1119629 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ + +src/main/resources/examples/_example_list + # Created by https://www.toptal.com/developers/gitignore/api/eclipse,visualstudiocode,intellij,java,gradle,maven,macos,netbeans # Edit at https://www.toptal.com/developers/gitignore?templates=eclipse,visualstudiocode,intellij,java,gradle,maven,macos,netbeans diff --git a/build.gradle b/build.gradle index ec04a7c..a12e02e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,8 +4,11 @@ plugins { id 'application' } +// Set project properties group 'dod-c3pu' -version '0.2' +version 'v0.1' +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 repositories { mavenCentral() @@ -92,10 +95,36 @@ tasks.withType(Test) { } } +task generateExampleList { + doLast { + // Define the directory and output file + def examplesDir = new File('src/main/resources/examples') + def outputFile = new File('src/main/resources/examples/_example_list') + + // Ensure the output file exists + outputFile.text = '' + + // List files and write their names to the output file + examplesDir.eachFile { File file -> + if (file.isFile() && file.name.endsWith('.txt')) { + outputFile.append(file.name + '\n') + } + } + } +} + +// Ensure this task is run before the jar is built +jar.dependsOn(generateExampleList) + + // Create a FAT JAR including all dependencies jar { + archiveBaseName.set('c3pu') + archiveVersion.set(version) manifest { - attributes 'Main-Class': 'view.Main' + attributes 'Main-Class': 'view.Main', + 'Implementation-Title': archiveBaseName.get(), + 'Implementation-Version': archiveVersion.get() } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } diff --git a/src/main/java/util/ExamplesHandler.java b/src/main/java/util/ExamplesHandler.java index 12ae5c8..ee80cfe 100644 --- a/src/main/java/util/ExamplesHandler.java +++ b/src/main/java/util/ExamplesHandler.java @@ -1,15 +1,19 @@ package util; import java.io.BufferedReader; -import java.io.File; import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.Path; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; /** @@ -19,67 +23,104 @@ public class ExamplesHandler { private static final String EXAMPLES_DIR = "/examples/"; + private static final String EXAMPLES_LIST_FILE = EXAMPLES_DIR + "_example_list"; - private static Map exampleMap = null; + private static Map exampleMap = null; private ExamplesHandler() {} - private static Map getExampleMap() { + private static Map getExampleMap() { if (exampleMap == null) { + // Read examples in one of two ways: + // 1. If running from from the source via Gradle, e.g. in your IDE or terminal, list the files + // in the examples dir and load them. + try { - exampleMap = - Arrays.stream( - Objects.requireNonNull( - new File(ExamplesHandler.class.getResource(EXAMPLES_DIR).toURI()) - .listFiles())) - .map(File::toPath) // Convert File to Path - .collect( - Collectors.toMap( - path -> { - // Process the file name to create a readable name - String name = path.getFileName().toString(); - name = name.substring(0, name.lastIndexOf('.')); - name = name.replace('_', ' '); - name = name.substring(0, 1).toUpperCase() + name.substring(1); - return name; - }, - path -> path // Use the Path object itself as the map value - )); - } catch (URISyntaxException e) { - e.printStackTrace(); + URI uri = ExamplesHandler.class.getResource(EXAMPLES_DIR).toURI(); + // Check if uri points to a file (directory), or just a resource in a JAR + if (uri.getScheme().equals("file")) { + exampleMap = + Files.list(Paths.get(uri)) + .filter(Files::isRegularFile) + .filter(path -> path.getFileName().toString().endsWith(".txt")) + .collect( + Collectors.toMap( + path -> nameFromPath(path.getFileName().toString()), + path -> { + // Load the file from the file system + try (BufferedReader reader = + new BufferedReader(new FileReader(path.toFile()))) { + return reader.lines().toArray(String[]::new); + } catch (Exception e) { + return new String[0]; + } + })); + } + } catch (URISyntaxException | IOException e) { + // Ignore + exampleMap = null; + } + + // 2. If running from a JAR, files in examples dir cannot be listed. Instead, first read the + // special file that contains their names, and then load them explicitly. + if (exampleMap == null) { + // Read the list of examples from the _example_list file using getResourceAsStream + try (BufferedReader reader = + new BufferedReader( + new InputStreamReader( + ExamplesHandler.class.getResourceAsStream(EXAMPLES_LIST_FILE)))) { + Map tmpMap = new HashMap<>(); + String line; + while ((line = reader.readLine()) != null) { + URL fileURL = ExamplesHandler.class.getResource(EXAMPLES_DIR + line); + if (fileURL != null) { + try (BufferedReader fileReader = + new BufferedReader(new InputStreamReader(fileURL.openStream()))) { + tmpMap.put(nameFromPath(line), fileReader.lines().toArray(String[]::new)); + } + } + } + exampleMap = tmpMap; + } catch (Exception e) { + // Ignore + exampleMap = null; + } + } + + if (exampleMap == null) { exampleMap = Collections.emptyMap(); } } return exampleMap; } + private static String nameFromPath(final String filename) { + // Process the file name to create a readable name + String name = filename; + name = name.substring(0, name.lastIndexOf('.')); + name = name.replace('_', ' '); + name = name.substring(0, 1).toUpperCase() + name.substring(1); + return name; + } + public static List getExampleNames() { return getExampleMap().keySet().stream().sorted().collect(Collectors.toList()); } public static String[] getExample(String name) { - Path path = getExampleMap().get(name); - if (path == null) { - return new String[0]; - } - try (BufferedReader reader = new BufferedReader(new FileReader(path.toFile()))) { - String[] lines = - reader - .lines() - .map(s -> s.split("(//|#|%)", 2)[0]) - .map(s -> s.replace(" ", "")) - .toArray(String[]::new); - // verify that each line contains only 0s and 1s, and is 8 characters long - for (int i = 0; i < lines.length; i++) { - String line = lines[i]; - if (line.length() != 8 || !line.matches("[01]+")) { - throw new IllegalArgumentException( - String.format("Invalid file format at line %d: '%s'", (i + 1), line)); - } + String[] lines = + Arrays.stream(getExampleMap().get(name)) + .map(s -> s.split("(//|#|%)", 2)[0]) + .map(s -> s.replace(" ", "")) + .toArray(String[]::new); + // verify that each line contains only 0s and 1s, and is 8 characters long + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + if (line.length() != 8 || !line.matches("[01]+")) { + throw new IllegalArgumentException( + String.format("Invalid file format at line %d: '%s'", (i + 1), line)); } - return lines; - } catch (Exception e) { - return new String[0]; } + return lines; } } diff --git a/src/main/java/view/HelpWindow.java b/src/main/java/view/HelpWindow.java index 588a479..029e0f4 100644 --- a/src/main/java/view/HelpWindow.java +++ b/src/main/java/view/HelpWindow.java @@ -4,12 +4,9 @@ import static util.LazySwing.inv; import java.io.IOException; -import java.net.URISyntaxException; +import java.io.InputStream; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JFrame; @@ -52,16 +49,14 @@ public HelpWindow(JFrame parent, Settings settings) { // Schedule a non-EDT task to load the html content new Thread( () -> { - Path path; - try { - path = Paths.get(helpUrl.toURI()); - byte[] fileBytes = Files.readAllBytes(path); + try (InputStream inputStream = getClass().getResourceAsStream("/help/help.html")) { + byte[] fileBytes = inputStream.readAllBytes(); inv( () -> { helpContent.setText(new String(fileBytes, StandardCharsets.UTF_8)); helpContent.setCaretPosition(0); }); - } catch (URISyntaxException | IOException e) { + } catch (IOException e) { inv( () -> helpContent.setText( diff --git a/todo.txt b/todo.txt index f75559b..67d8358 100644 --- a/todo.txt +++ b/todo.txt @@ -102,6 +102,8 @@ [x] Update example programs, after instruction changes (again...) [] Add config/view option to turn off auto scroll to active cell [] Think about: Should conditional jump use another dst register, e.g. R2? (Instead of RES) +[x] Resources failed to load when building and running from JAR. Fix! +[] Set up a Logger, that can collect errors in a local file.$ [] 0100 1010 0001 0000