diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java index b7ee3097e4e621375eba2c7e42249a2520e1cdc1..66ed57bf2762012f4cf3b8e19dc5198f214cf1e0 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java @@ -42,6 +42,7 @@ import tec.units.ri.unit.MetricPrefix; import javax.measure.Quantity; import javax.measure.quantity.Length; import java.io.BufferedReader; +import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; @@ -166,9 +167,30 @@ public final class App { } // Config Parsing + String configPath; + if (!settingsReader.isPresent(SettingType.PRINTER_CONFIG_PATH)) { // TODO: exception if missing this argument + configPath = this.getClass().getResource("/config/index_everest_d_v4.properties").getFile(); + mLogger.info("Using default file: " + configPath); + if (!(new File(configPath)).isFile()) { + mLogger.info("Does not exist!"); + } + } else { + configPath = settingsReader.getSetting(SettingType.PRINTER_CONFIG_PATH).get(); + } + + /* + GeneralResource testResource = new GeneralResource("/config/default.properties"); + if (testResource.isValidExternalFile()) { + File testFile = testResource.asValidExternalFile(); + } else { + InputStream testStream = testResource.asInputStream(); + } + + */ + JavaPropertiesConfigurationParser configParser = new JavaPropertiesConfigurationParser( - getClass().getClassLoader().getResource("config/index_everest_d_v4.properties").getFile(), - getClass().getClassLoader().getResource("config/default.properties").getFile() + configPath, + "/config/default.properties" ); Printer indexV4Printer = configParser.getPrinter(); Format a4Format = configParser.getFormat("A4"); diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/GeneralResource.java b/src/main/java/de/tudresden/inf/mci/brailleplot/GeneralResource.java new file mode 100644 index 0000000000000000000000000000000000000000..cf5a2bf49068fae604358ded4720ab22054cb765 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/GeneralResource.java @@ -0,0 +1,137 @@ +package de.tudresden.inf.mci.brailleplot; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.UUID; + +/** + * This class is used to use files & resources independent from the application running from packed jar or not. + * @author Leonard Kupper + * @version 2019-09-10 + */ +public final class GeneralResource { + + private String mResourcePath; + private boolean validExternalFile = false; + private boolean validPackedResource = false; + Logger mLogger = LoggerFactory.getLogger(this.getClass()); + + /** + * Create a resource from a file path or resource classpath. + * @param resourcePath Relative or local path to a file (e.g. C:\example.txt) in the filesystem or classpath pointing to (packed) resource location (e.g. /config/default.properties) + * @throws IOException If the given path neither determines a valid external file, nor a valid resource. + */ + public GeneralResource(final String resourcePath) throws IOException { + this(resourcePath, null); + } + + /** + * Create a resource from a file path or resource classpath. + * @param resourcePath Relative or local path to a file (e.g. C:\example.txt) in the filesystem or classpath pointing to (packed) resource location (e.g. /config/default.properties) + * @param searchPath Relative or local path to be used as base for interpreting the resourcePath relatively to additionally. + * @throws IOException If the given path neither determines a valid external file, nor a valid resource. + */ + public GeneralResource(final String resourcePath, final String searchPath) throws IOException { + File checkFile = new File(resourcePath); + mLogger.info("checking referenced path: " + checkFile); + if (checkFile.isFile()) { + mLogger.info("interpreting path as file: " + checkFile.getCanonicalPath()); + mResourcePath = checkFile.getCanonicalPath(); + validExternalFile = true; + } + checkFile = checkFile.getAbsoluteFile(); + mLogger.info("checking referenced path as absolute path: " + checkFile); + if (checkFile.isFile()) { + mLogger.info("interpreting path as absolute file: " + checkFile.getCanonicalPath()); + mResourcePath = checkFile.getCanonicalPath(); + validExternalFile = true; + } + if (Objects.nonNull(searchPath)) { + checkFile = new File(searchPath + File.separator + resourcePath); + mLogger.info("looking for referenced path in search path: " + checkFile); + if (checkFile.isFile()) { + mLogger.info("interpreting path as search path relative file: " + checkFile.getCanonicalPath()); + mResourcePath = checkFile.getCanonicalPath(); + validExternalFile = true; + } + } + String resourceClassPath = resourcePath.replace("\\", "/"); // classpaths are always separated by forward slash + InputStream checkStream = getClass().getResourceAsStream(resourceClassPath); + mLogger.info("checking referenced path as resource: " + resourceClassPath); + if (Objects.nonNull(checkStream)) { + mLogger.info("interpreting path as resource stream: " + resourceClassPath); + mResourcePath = resourceClassPath; + validPackedResource = true; + } + if (Objects.nonNull(searchPath)) { + String relativeResourcePath = new File(searchPath + File.separator + resourceClassPath).toPath().normalize().toString(); + relativeResourcePath = relativeResourcePath.replace("\\", "/"); + checkStream = getClass().getResourceAsStream(relativeResourcePath); + mLogger.info("checking referenced path as search path relative resource: " + relativeResourcePath); + if (Objects.nonNull(checkStream)) { + mLogger.info("interpreting path as resource stream: " + relativeResourcePath); + mResourcePath = relativeResourcePath; + validPackedResource = true; + } + } + if (!(isValidExternalFile() || isValidPackedResource())) { + throw new FileNotFoundException("Not recognized as valid file or resource: " + resourcePath); + } + } + + public boolean isValidExternalFile() { + return validExternalFile; + } + + public boolean isValidPackedResource() { + return validPackedResource; + } + + public InputStream asInputStream() { + try { + if (isValidExternalFile()) { + return new FileInputStream(getResourcePath()); + } + return getClass().getResourceAsStream(mResourcePath); + } catch (Exception e) { + throw new RuntimeException("Error while opening resource as stream: ", e); + } + } + + public File asValidExternalFile() { + if (!isValidExternalFile()) { + throw new IllegalArgumentException("Not a valid external file: " + getResourcePath()); + } + return new File(getResourcePath()); + } + + public String getResourcePath() { + return mResourcePath; + } + + public File getFileOrExportResource() { + if (!isValidPackedResource()) { + return asValidExternalFile(); + } else { + try { + File tmpOut = File.createTempFile("resource_" + UUID.randomUUID(), "tmp"); + byte[] content = asInputStream().readAllBytes(); + FileOutputStream outputStream = new FileOutputStream(tmpOut); + outputStream.write(content); + outputStream.close(); + return tmpOut; + } catch (IOException e) { + throw new RuntimeException("Error while exporting resource: ", e); + } + } + } + +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java index 54284b1a68598478f5bf7df98ad5b562976b8268..bdd14b694134e933355410cd845d1c23ee7b9b7f 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/ConfigurationParser.java @@ -1,13 +1,18 @@ package de.tudresden.inf.mci.brailleplot.configparser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; /** @@ -28,8 +33,10 @@ public abstract class ConfigurationParser { private Printer mDefaultPrinter; private Format mDefaultFormat; - ConfigurationParser() { + private final Logger mLogger; + ConfigurationParser() { + mLogger = LoggerFactory.getLogger(this.getClass()); }; /** @@ -42,7 +49,7 @@ public abstract class ConfigurationParser { * @throws ConfigurationParsingException On any error while accessing the configuration file or syntax. * @throws ConfigurationValidationException On any error while checking the parsed properties validity. */ - protected abstract void parse(FileInputStream input) throws ConfigurationParsingException, ConfigurationValidationException; + protected abstract void parse(InputStream input) throws ConfigurationParsingException, ConfigurationValidationException; /** @@ -147,8 +154,8 @@ public abstract class ConfigurationParser { mFormatProperties.clear(); // load and parse file mCurrentConfigFile = new File(filePath); - FileInputStream input = openInputStream(filePath); - getValidator().setSearchPath(getConfigFile().getParentFile().getAbsolutePath()); + InputStream input = openInputStream(filePath); + getValidator().setSearchPath(Objects.requireNonNullElse(getConfigFile().getParent(), "")); parse(input); closeInputStream(input); // build printer object from added properties @@ -178,11 +185,23 @@ public abstract class ConfigurationParser { * @return A {@link FileInputStream} for the given file path. * @throws ConfigurationParsingException On any error while opening the stream. (e.g. missing file) */ - final FileInputStream openInputStream(final String filePath) throws ConfigurationParsingException { + final InputStream openInputStream(final String filePath) throws ConfigurationParsingException { + // This has to take the fact into consideration, that a resource from a packaged jar is not a real file in + // the file system and must be read via classloader resource stream. We want to be able to process files as + // well as packed resources. try { + // first try to read as file + mLogger.info("trying to open as file: " + filePath); return new FileInputStream(filePath); } catch (IOException e) { - throw new ConfigurationParsingException("Unable to read configuration file", e); + // if that fails, try to read as resource + mLogger.info("trying to open as resource: " + filePath); + InputStream resourceStream = this.getClass().getResourceAsStream(filePath); + if (Objects.isNull(resourceStream)) { + // if that also fails: + throw new ConfigurationParsingException("Unable to read configuration file / resource.", e); + } + return resourceStream; } } @@ -191,7 +210,7 @@ public abstract class ConfigurationParser { * @param input The {@link FileInputStream} to be closed. * @throws ConfigurationParsingException On any error while closing the stream. */ - final void closeInputStream(final FileInputStream input) throws ConfigurationParsingException { + final void closeInputStream(final InputStream input) throws ConfigurationParsingException { try { input.close(); } catch (IOException e) { diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java index 31e9fc794280ada718e60d3dda1bbc635a4ec1c5..39611e2397549052991b5f6823219e8e2453788b 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParser.java @@ -1,8 +1,12 @@ package de.tudresden.inf.mci.brailleplot.configparser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; -import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; import java.util.Properties; import java.util.Stack; @@ -14,6 +18,7 @@ import java.util.Stack; public final class JavaPropertiesConfigurationParser extends ConfigurationParser { Stack<File> mInclusionStack = new Stack<>(); + private final Logger mLogger; /** * Constructor. @@ -28,6 +33,7 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser final String filePath, final String defaultPath ) throws ConfigurationParsingException, ConfigurationValidationException { + mLogger = LoggerFactory.getLogger(this.getClass()); setValidator(new JavaPropertiesConfigurationValidator()); parseConfigFile(defaultPath, false); setDefaults(getPrinter(), getFormat("default")); @@ -41,7 +47,7 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser * @throws ConfigurationParsingException On any error while accessing the configuration file or syntax. * @throws ConfigurationValidationException On any error while checking the parsed properties validity. */ - protected void parse(final FileInputStream input) throws ConfigurationParsingException, ConfigurationValidationException { + protected void parse(final InputStream input) throws ConfigurationParsingException, ConfigurationValidationException { // Create property instance for current recursion level Properties properties = new Properties(); try { @@ -77,7 +83,7 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser if (mInclusionStack.empty()) { mInclusionStack.push(getConfigFile()); } - File includeFile, parentFile = mInclusionStack.peek().getParentFile(); + File includeFile, parentFile = Objects.requireNonNullElse(mInclusionStack.peek().getParentFile(), new File("")); try { String findIncludePath = parentFile.getAbsolutePath() + File.separator + includeName.trim(); File abstractPath = new File(findIncludePath); @@ -94,13 +100,14 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser if (mInclusionStack.contains(includeFile)) { continue; } - FileInputStream includeInput = openInputStream(includeFile.getAbsolutePath()); + InputStream includeInput = openInputStream(includeFile.getAbsolutePath()); try { mInclusionStack.push(includeFile); - getValidator().setSearchPath(mInclusionStack.peek().getParentFile().getAbsolutePath()); + mLogger.info("Including config file: " + includeFile); + getValidator().setSearchPath(Objects.requireNonNullElse(mInclusionStack.peek().getParent(), "")); parse(includeInput); mInclusionStack.pop(); - getValidator().setSearchPath(mInclusionStack.peek().getParentFile().getAbsolutePath()); + getValidator().setSearchPath(Objects.requireNonNullElse(mInclusionStack.peek().getParent(), "")); } finally { closeInputStream(includeInput); } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java index fac80641bb465377dc29171a9e6ae897d4150121..33542e992ff20efb8a6ac365f4bedba66922259f 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java @@ -1,13 +1,11 @@ package de.tudresden.inf.mci.brailleplot.configparser; +import de.tudresden.inf.mci.brailleplot.GeneralResource; import de.tudresden.inf.mci.brailleplot.printerbackend.PrinterCapability; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -87,7 +85,7 @@ public final class JavaPropertiesConfigurationValidator implements Configuration private String interpretProperty(final String propertyName, final String value) throws ConfigurationValidationException { try { switch (propertyName) { - case "brailletable": return checkFileLocation(value); + case "brailletable": return new GeneralResource(value, mSearchPath).getResourcePath(); default: return value; } } catch (Exception e) { @@ -95,30 +93,31 @@ public final class JavaPropertiesConfigurationValidator implements Configuration } } - /** - * This will first check the given file path if it exists as it is, else interpret as absolute, else relative to the set search path. - * @param path The file path to be checked. (Must be a file, not directory!) - * @return The interpreted path string to an existing file. - * @throws FileNotFoundException If all options to interpret the path string result in non-existing files. - */ - private String checkFileLocation(final String path) throws FileNotFoundException { +/** + * This will first check the given file path if it exists as it is, else interpret as absolute, else relative to the set search path. + * @param path The file path to be checked. (Must be a file, not directory!) + * @return The interpreted path string to an existing file. + * @throws IOException If all options to interpret the path string result in non-existing files. + */ + /* + private String checkFileLocation(final String path) throws IOException { File checkFile = new File(path); mLogger.info("checking referenced path: " + checkFile); if (checkFile.isFile()) { - mLogger.info("interpreting path as file: " + checkFile.getAbsolutePath()); + mLogger.info("interpreting path as file: " + checkFile.getCanonicalPath()); return checkFile.getAbsolutePath(); } checkFile = checkFile.getAbsoluteFile(); mLogger.info("checking referenced path as absolute path: " + checkFile); if (checkFile.isFile()) { - mLogger.info("interpreting path as absolute file: " + checkFile.getAbsolutePath()); + mLogger.info("interpreting path as absolute file: " + checkFile.getCanonicalPath()); return checkFile.getAbsolutePath(); } if (Objects.nonNull(mSearchPath)) { checkFile = new File(mSearchPath + File.separator + path); mLogger.info("looking for referenced path in search path: " + checkFile); if (checkFile.isFile()) { - mLogger.info("interpreting path as search path relative file: " + checkFile.getAbsolutePath()); + mLogger.info("interpreting path as search path relative file: " + checkFile.getCanonicalPath()); return checkFile.getAbsolutePath(); } } @@ -128,8 +127,16 @@ public final class JavaPropertiesConfigurationValidator implements Configuration mLogger.info("interpreting path as resource stream: " + path); return path; } + String relativeResourcePath = mSearchPath + File.separator + path; + checkStream = getClass().getClassLoader().getResourceAsStream(relativeResourcePath); + mLogger.info("checking referenced path as relative resource: " + relativeResourcePath); + if (Objects.nonNull(checkStream)) { + mLogger.info("interpreting path as resource stream: " + relativeResourcePath); + return path; + } throw new FileNotFoundException("File/Resource not found: " + path); } + */ @Override public void setSearchPath(final String searchPath) { diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java index 9863e424b3fac215c7d3c15c8546cddbdb193f76..b4935de4f45a2f7056d7cfdd74293ae69013e2aa 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java @@ -1,5 +1,6 @@ package de.tudresden.inf.mci.brailleplot.rendering; +import de.tudresden.inf.mci.brailleplot.GeneralResource; import de.tudresden.inf.mci.brailleplot.brailleparser.AbstractBrailleTableParser; import de.tudresden.inf.mci.brailleplot.configparser.Printer; import de.tudresden.inf.mci.brailleplot.layout.InsufficientRenderingAreaException; @@ -12,6 +13,8 @@ import org.liblouis.TranslationException; import org.liblouis.TranslationResult; import org.liblouis.Translator; +import java.io.File; +import java.nio.file.Files; import java.util.Objects; import static java.lang.Math.ceil; @@ -45,9 +48,11 @@ public class LiblouisBrailleTextRasterizer implements Rasterizer<BrailleText> { throw new RuntimeException(e); } try { - mTranslator = new Translator("src\\main\\resources\\mapping\\liblouis\\de-g0.utb"); + GeneralResource table = new GeneralResource("/mapping/liblouis/de-g0.utb"); + File tableFile = table.getFileOrExportResource(); + mTranslator = new Translator(tableFile.getAbsolutePath()); } catch (Exception e) { - throw new RuntimeException(e.getCause()); + throw new RuntimeException("Error while creating liblouis translator:", e); } }