diff --git a/src/integrationTest/java/PrintableDataExporterIntegTest.java b/src/integrationTest/java/PrintableDataExporterIntegTest.java index 5d887597aed7972e852c97e3c6d3e2acfeac750e..39ddef7d56907c7a2f3246c2cfa7bdc0bf8d4d60 100644 --- a/src/integrationTest/java/PrintableDataExporterIntegTest.java +++ b/src/integrationTest/java/PrintableDataExporterIntegTest.java @@ -14,10 +14,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import javax.print.DocFlavor; -import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.URL; /** * Integrationtests for the components PrintableData and Exporter. @@ -45,8 +45,8 @@ public class PrintableDataExporterIntegTest { @BeforeAll public static void setUp() { Assertions.assertDoesNotThrow(() -> { - String correct = getResource("config/correct.properties").getAbsolutePath(); - String standard = getResource("config/default.properties").getAbsolutePath(); + URL correct = ClassLoader.getSystemClassLoader().getResource("config/correct.properties"); + URL standard = ClassLoader.getSystemClassLoader().getResource("config/default.properties"); JavaPropertiesConfigurationParser configParser = new JavaPropertiesConfigurationParser(correct, standard); printer = configParser.getPrinter(); printer.getProperty("brailletable").toString(); @@ -55,11 +55,6 @@ public class PrintableDataExporterIntegTest { }); } - public static File getResource(String fileName) { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - File resourceFile = new File(classLoader.getResource(fileName).getFile()); - return resourceFile; - } /** * Unittest/Integrationtest for the private Print method with a Null Servive. * Expected: Nullpointerexception. 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 fea3496dbc2042a5b1b14800bbb79adf3183b68e..ada2210886f06dae999db4d43f658ab5e2bd17dd 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java @@ -45,6 +45,7 @@ import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.net.URL; import java.util.Optional; import java.util.concurrent.ConcurrentLinkedDeque; @@ -166,18 +167,15 @@ public final class App { } // Config Parsing - String configPath; + URL configPath; if (!settingsReader.isPresent(SettingType.PRINTER_CONFIG_PATH)) { // TODO: exception if missing this argument, until then use default location for test runs + configPath = getClass().getResource("/config/index_everest_d_v4.properties"); mLogger.warn("ATTENTION! Using default specific config from resources. Please remove default config behavior before packaging the jar."); - configPath = getClass().getResource("/config/index_everest_d_v4.properties").getFile(); } else { - configPath = settingsReader.getSetting(SettingType.PRINTER_CONFIG_PATH).get(); + configPath = new URL(settingsReader.getSetting(SettingType.PRINTER_CONFIG_PATH).get()); } - JavaPropertiesConfigurationParser configParser = new JavaPropertiesConfigurationParser( - configPath, - "/config/default.properties" - ); + JavaPropertiesConfigurationParser configParser = new JavaPropertiesConfigurationParser(configPath, getClass().getClassLoader().getResource("config/default.properties")); Printer indexV4Printer = configParser.getPrinter(); Format a4Format = configParser.getFormat("A4"); diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/brailleparser/PropertiesParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/brailleparser/PropertiesParser.java index ed095f3b562f3e8e5b35598dc78aa3a19e40303e..41869b198f888bfceaeb41b406399c9b09614fe9 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/brailleparser/PropertiesParser.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/brailleparser/PropertiesParser.java @@ -1,6 +1,6 @@ package de.tudresden.inf.mci.brailleplot.brailleparser; -import de.tudresden.inf.mci.brailleplot.GeneralResource; +import de.tudresden.inf.mci.brailleplot.util.GeneralResource; import java.io.InputStream; import java.util.Objects; 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 bdd14b694134e933355410cd845d1c23ee7b9b7f..4ee9bc3995deda6577681d1e6220b4ac050ff3fb 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 @@ -3,10 +3,13 @@ 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.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -18,8 +21,8 @@ import java.util.Set; /** * Abstract parser for configuration files. Interface for {@link Printer} and multiple {@link Format} configurations. * Must be extended to implement a concrete parsing algorithm for a specific file format. - * @author Leonard Kupper - * @version 2019.07.18 + * @author Leonard Kupper, Georg Graßnick + * @version 2019.09.23 */ public abstract class ConfigurationParser { @@ -27,41 +30,32 @@ public abstract class ConfigurationParser { private ConfigurationValidator mValidator; private Printer mPrinter; private Map<String, Format> mFormats = new HashMap<>(); - private File mCurrentConfigFile; private List<PrinterProperty> mPrinterProperties = new ArrayList<>(); private Map<String, List<FormatProperty>> mFormatProperties = new HashMap<>(); private Printer mDefaultPrinter; private Format mDefaultFormat; - private final Logger mLogger; + protected final Logger mLogger = LoggerFactory.getLogger(getClass()); ConfigurationParser() { - mLogger = LoggerFactory.getLogger(this.getClass()); - }; + } /** * Internal algorithm used for parsing of the configuration file. * Implement this method by parsing information from the given input stream, optionally validating it (see {@link ConfigurationValidator}), * constructing {@link PrinterProperty} and {@link FormatProperty} objects from this information and adding them with the methods * {@link #addProperty(PrinterProperty)} and {@link #addProperty(FormatProperty)}. - * This method is called by ({@link #parseConfigFile(String, boolean)}). - * @param input The input stream to read the configuration from. - * @throws ConfigurationParsingException On any error while accessing the configuration file or syntax. + * This method is called by ({@link #parseConfigFile(InputStream, URL, boolean)}). + * @param inStream The input stream to read the configuration from. + * @param path The URL identifying the location of the source of the {@link InputStream}. Required the inclusion of configurations from relative paths. + * @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(InputStream input) throws ConfigurationParsingException, ConfigurationValidationException; - - - /** - * Get the current configuration file. - * @return A {@link File} object representing the configuration file. - */ - public final File getConfigFile() { - return mCurrentConfigFile; - } + protected abstract void parse(InputStream inStream, URL path) throws ConfigurationParsingException, ConfigurationValidationException; /** * Get the printer configuration. + * * @return A {@link Printer} object, representing the printers properties. */ public final Printer getPrinter() { @@ -70,6 +64,7 @@ public abstract class ConfigurationParser { /** * Get the names of all available format configurations. + * * @return A {@link Set}<{@link String}> containing the name of each format. */ public final Set<String> getFormatNames() { @@ -78,6 +73,7 @@ public abstract class ConfigurationParser { /** * Get a specific format configuration. + * * @param formatName The name of the format. * @return A {@link Format} object, representing the formats properties. * @throws NoSuchElementException If no format has the specified name. @@ -93,6 +89,7 @@ public abstract class ConfigurationParser { /** * Set a {@link ConfigurationValidator} for this parser. * This method should be called inside the concrete parsers constructor + * * @param validator The {@link ConfigurationValidator} object. */ protected void setValidator(final ConfigurationValidator validator) { @@ -101,6 +98,7 @@ public abstract class ConfigurationParser { /** * Get the {@link ConfigurationValidator} for this parser. + * * @return A {@link ConfigurationValidator} object. */ protected ConfigurationValidator getValidator() { @@ -109,6 +107,7 @@ public abstract class ConfigurationParser { /** * Add a general printer property to the internal printer configuration representation. + * * @param property The represented property of the printer. */ protected void addProperty(final PrinterProperty property) { @@ -117,6 +116,7 @@ public abstract class ConfigurationParser { /** * Add a specific format property to the internal list of format configuration representation. + * * @param property The represented property of a specific format. */ protected void addProperty(final FormatProperty property) { @@ -130,34 +130,70 @@ public abstract class ConfigurationParser { /** * Set the optional default configurations for {@link Printer} and {@link Format} objects created by this parser. * This method should be called inside the concrete parsers constructor. + * * @param defaultPrinter A {@link Printer} object containing the default properties or null for no default to be set. - * @param defaultFormat A {@link Format} object containing the default properties or null for no default to be set. + * @param defaultFormat A {@link Format} object containing the default properties or null for no default to be set. */ protected final void setDefaults(final Printer defaultPrinter, final Format defaultFormat) { mDefaultPrinter = defaultPrinter; mDefaultFormat = defaultFormat; } + /** + * Parse a configuration file from the resource folder. + * @param resource The URL identifying the configuration file. + * @param assertCompleteness Signals whether to check for existence of all required properties or not + * @throws ConfigurationParsingException if an error occurred reading from the resource identified by the URL resource parameter + * @throws ConfigurationValidationException if an exception occurred while calling the {@link JavaPropertiesConfigurationValidator} + */ + protected final void parseConfigFileFromResource(final URL resource, final boolean assertCompleteness) throws ConfigurationParsingException, ConfigurationValidationException { + Objects.requireNonNull(resource); + + mLogger.debug("Starting parsing properties file from java resources: \"{}\"", resource); + + try { + parseConfigFile(resource.openStream(), getParentUrl(resource), assertCompleteness); + } catch (IOException e) { + throw new ConfigurationParsingException("Could not open resource at \"" + resource.toString() + "\"", e); + } + } + + /** + * Parse a configuration file from the file system. + * @param filePath The location of the file to parse. + * @param assertCompleteness Signals whether to check for existence of all required properties or not + * @throws ConfigurationParsingException if the file could not be read correctly + * @throws ConfigurationValidationException if an exception occurred while calling the {@link JavaPropertiesConfigurationValidator} + */ + protected final void parseConfigFileFromFileSystem(final Path filePath, final boolean assertCompleteness) throws ConfigurationParsingException, ConfigurationValidationException { + Objects.requireNonNull(filePath); + + mLogger.debug("Starting parsing properties file from file system: \"{}\"", filePath); + + try { + parseConfigFile(new FileInputStream(filePath.toFile()), filePath.toFile().toURI().toURL(), assertCompleteness); + } catch (FileNotFoundException | MalformedURLException e) { + throw new ConfigurationParsingException("Configuration file could not be read at \"" + filePath.toString() + "\""); + } + } + /** * Parse the specified configuration file. * This method should be called inside the concrete parsers constructor after the optional default configurations * ({@link #setDefaults(Printer, Format)}) and the validator ({@link #setValidator(ConfigurationValidator)}) have been set. - * @param filePath The configuration file to be parsed. The type depends on the concrete implementation of the parser. + * @param config The {@link InputStream} to be parsed * @param assertCompleteness Signals whether to check for existence of all required properties or not. - * @throws ConfigurationParsingException On any error while accessing the configuration file or syntax + * @throws ConfigurationParsingException On any error while accessing the configuration file or syntax * @throws ConfigurationValidationException On any error while checking the parsed properties validity. */ - protected final void parseConfigFile(final String filePath, final boolean assertCompleteness) + private void parseConfigFile(final InputStream config, final URL path, final boolean assertCompleteness) throws ConfigurationParsingException, ConfigurationValidationException { // reset internal property buffer mPrinterProperties.clear(); mFormatProperties.clear(); + mValidator.setSearchPath(getPath(path)); // load and parse file - mCurrentConfigFile = new File(filePath); - InputStream input = openInputStream(filePath); - getValidator().setSearchPath(Objects.requireNonNullElse(getConfigFile().getParent(), "")); - parse(input); - closeInputStream(input); + parse(config, path); // build printer object from added properties mPrinter = new Printer(mPrinterProperties); if (mDefaultPrinter != null) { @@ -180,41 +216,29 @@ public abstract class ConfigurationParser { } /** - * Opens the input stream for the given file path. - * @param filePath The file to read from. - * @return A {@link FileInputStream} for the given file path. - * @throws ConfigurationParsingException On any error while opening the stream. (e.g. missing file) + * Returns the URL to the parent directory of a File / Resource. + * @param resourcePath The URL to analyze. + * @return The URL to the parent directory of the specified URL. + * @throws ConfigurationParsingException if the generated URL is not a valid URL. */ - 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. + private static URL getParentUrl(final URL resourcePath) throws ConfigurationParsingException { + String fileString = resourcePath.getPath(); + String parentString = fileString.substring(0, fileString.lastIndexOf("/")); try { - // first try to read as file - mLogger.info("trying to open as file: " + filePath); - return new FileInputStream(filePath); - } catch (IOException 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; + return new URL(resourcePath.getProtocol(), resourcePath.getHost(), parentString); + } catch (MalformedURLException e) { + throw new ConfigurationParsingException("Could not create URL to parent path", e); } } /** - * Closes the given input stream. - * @param input The {@link FileInputStream} to be closed. - * @throws ConfigurationParsingException On any error while closing the stream. + * Return a String representation of the path of a {@link URL}. + * Strips the {@literal "}file:{@literal "} prefix from an URL, if it exist. + * @param url The URL that needs to be stripped + * @return The String representation of the path of a URL where the leading {@literal "}file:{@literal "} prefix is stripped. */ - final void closeInputStream(final InputStream input) throws ConfigurationParsingException { - try { - input.close(); - } catch (IOException e) { - throw new ConfigurationParsingException("Unable to close input stream.", e); - } + private static String getPath(final URL url) { + String urlString = url.getPath(); + return urlString.replaceAll("^file:", ""); } } 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 39611e2397549052991b5f6823219e8e2453788b..0cb2a42fd35a4a380ed3ac950a99d9c8b8e32ab2 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,24 +1,24 @@ package de.tudresden.inf.mci.brailleplot.configparser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; +import java.io.BufferedInputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; import java.util.Objects; import java.util.Properties; -import java.util.Stack; /** * Concrete parser for configuration files in Java Property File format. - * @author Leonard Kupper - * @version 2019.07.18 + * @author Leonard Kupper, Georg Graßnick + * @version 2019.09.23 */ public final class JavaPropertiesConfigurationParser extends ConfigurationParser { - Stack<File> mInclusionStack = new Stack<>(); - private final Logger mLogger; + private static final String INCLUDE_FILE_EXTENSION = ".properties"; /** * Constructor. @@ -29,39 +29,67 @@ 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. */ - public JavaPropertiesConfigurationParser( - final String filePath, - final String defaultPath - ) throws ConfigurationParsingException, ConfigurationValidationException { - mLogger = LoggerFactory.getLogger(this.getClass()); - setValidator(new JavaPropertiesConfigurationValidator()); - parseConfigFile(defaultPath, false); + public JavaPropertiesConfigurationParser(final Path filePath, final URL defaultPath) throws ConfigurationParsingException, ConfigurationValidationException { + setup(); + parseConfigFileFromResource(defaultPath, false); + setDefaults(getPrinter(), getFormat("default")); + parseConfigFileFromFileSystem(filePath, true); + } + + public JavaPropertiesConfigurationParser(final Path filePath, final Path defaultPath) throws ConfigurationParsingException, ConfigurationValidationException { + setup(); + parseConfigFileFromFileSystem(defaultPath, false); + setDefaults(getPrinter(), getFormat("default")); + parseConfigFileFromFileSystem(filePath, true); + } + + public JavaPropertiesConfigurationParser(final URL filePath, final Path defaultPath) throws ConfigurationParsingException, ConfigurationValidationException { + setup(); + parseConfigFileFromFileSystem(defaultPath, false); + setDefaults(getPrinter(), getFormat("default")); + parseConfigFileFromResource(filePath, true); + } + + public JavaPropertiesConfigurationParser(final URL filePath, final URL defaultPath) throws ConfigurationParsingException, ConfigurationValidationException { + setup(); + parseConfigFileFromResource(defaultPath, false); setDefaults(getPrinter(), getFormat("default")); - parseConfigFile(filePath, true); + parseConfigFileFromResource(filePath, true); + } + + private void setup() { + setValidator(new JavaPropertiesConfigurationValidator()); } /** * Concrete internal algorithm used for parsing the Java Property File. - * This method is called by ({@link #parseConfigFile(String, boolean)}) and will call itself recursively for every included file. - * @param input The input stream to read the configuration properties from. + * This method is called by ({@link ConfigurationParser#parseConfigFileFromFileSystem(Path, boolean)} (InputStream, boolean)}) + * or {@link ConfigurationParser#parseConfigFileFromResource(URL, boolean)} where it was called to include other configurations. + * @param inStream The fileToParse stream to read the configuration properties from. + * @param path The URL identifying the location of the source of the {@link InputStream}. Required the inclusion of configurations from relative paths. * @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 InputStream input) throws ConfigurationParsingException, ConfigurationValidationException { + protected void parse(final InputStream inStream, final URL path) throws ConfigurationParsingException, ConfigurationValidationException { + Objects.requireNonNull(inStream); + Objects.requireNonNull(path); // Create property instance for current recursion level Properties properties = new Properties(); + try { // Load properties from the .properties file - properties.load(input); + properties.load(inStream); } catch (IOException e) { - throw new ConfigurationParsingException("Unable to load properties from file.", e); + throw new ConfigurationParsingException("Unable to load properties from file \"" + inStream + "\"", e); } // Iterate over all properties as key -> value pairs for (String key : properties.stringPropertyNames()) { String value = properties.getProperty(key); // check for special property key: 'include' - if (("include").equals(key.toLowerCase())) { - includeFiles(value); + if (key.equalsIgnoreCase("include")) { + includeResource(value, path); + } else if (key.equalsIgnoreCase("include-file")) { + includeFiles(value, path); } else { parseProperty(key, value); } @@ -69,6 +97,7 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser } private void parseProperty(final String key, final String value) throws ConfigurationValidationException { + mLogger.trace("Parsed property \"{}\" with value \"{}\"", key, value); ValidProperty property = getValidator().validate(key, value); if (property instanceof FormatProperty) { addProperty((FormatProperty) property); @@ -78,38 +107,59 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser } } - private void includeFiles(final String fileList) throws ConfigurationParsingException, ConfigurationValidationException { - for (String includeName : fileList.split(",")) { - if (mInclusionStack.empty()) { - mInclusionStack.push(getConfigFile()); - } - File includeFile, parentFile = Objects.requireNonNullElse(mInclusionStack.peek().getParentFile(), new File("")); + /** + * Recursively parses the configuration file. + * This method handles files on the local file system. + * @param fileList The string representations of the paths to include. + * @param parentUrl The URL identifying the context from where the method was called. Required to construct relative paths. + * @throws ConfigurationParsingException If something went wrong while reading from the included files. + * @throws ConfigurationValidationException On any error while checking the parsed properties validity. + */ + private void includeFiles(final String fileList, final URL parentUrl) throws ConfigurationParsingException, ConfigurationValidationException { + for (String s : fileList.split(",")) { + + Path parentPath = null; try { - String findIncludePath = parentFile.getAbsolutePath() + File.separator + includeName.trim(); - File abstractPath = new File(findIncludePath); - if (!abstractPath.exists()) { - abstractPath = new File(findIncludePath + ".properties"); - } - includeFile = abstractPath.getCanonicalFile(); - if (!includeFile.isFile()) { - throw new ConfigurationParsingException("Given include path is not a file: " + includeFile); - } - } catch (IOException e) { - throw new ConfigurationParsingException("Can not find include file.", e); + parentPath = Path.of(parentUrl.toURI()); + } catch (URISyntaxException e) { + throw new ConfigurationParsingException("Could not generate URI", e); } - if (mInclusionStack.contains(includeFile)) { - continue; + Path newPath = parentPath.getParent().resolve(s.trim() + INCLUDE_FILE_EXTENSION); + String newPathString = newPath.toAbsolutePath().toString(); + + mLogger.debug("Prepare recursive parsing of properties file in the file system for file \"{}\"", newPathString); + + try (InputStream is = new BufferedInputStream(new FileInputStream(newPathString))) { + parse(is, newPath.toUri().toURL()); + } catch (IOException e) { + throw new ConfigurationParsingException("Could not open include file", e); } - InputStream includeInput = openInputStream(includeFile.getAbsolutePath()); + } + } + + /** + * Recursively parse configuration from a java resource. + * @param fileList The string representations of the paths to include. + * @param parentUrl The URL identifying the context from where the method was called. Required to construct relative paths. + * @throws ConfigurationParsingException If errors occurred while reading from a resource. + * @throws ConfigurationValidationException On any error while checking the parsed properties validity. + */ + private void includeResource(final String fileList, final URL parentUrl) throws ConfigurationParsingException, ConfigurationValidationException { + for (String s : fileList.split(",")) { + + URL newUrl = null; try { - mInclusionStack.push(includeFile); - mLogger.info("Including config file: " + includeFile); - getValidator().setSearchPath(Objects.requireNonNullElse(mInclusionStack.peek().getParent(), "")); - parse(includeInput); - mInclusionStack.pop(); - getValidator().setSearchPath(Objects.requireNonNullElse(mInclusionStack.peek().getParent(), "")); - } finally { - closeInputStream(includeInput); + newUrl = new URL(parentUrl + "/" + s.trim() + INCLUDE_FILE_EXTENSION); + } catch (MalformedURLException e) { + throw new ConfigurationParsingException("Could not generate URI", e); + } + + mLogger.debug("Prepare recursive parsing of properties file in the java resources at \"{}\"", newUrl); + + try (InputStream is = newUrl.openStream()) { + parse(is, newUrl); + } catch (IOException e) { + throw new ConfigurationParsingException("Could not open include resource", e); } } } 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 d8f5ecf10e7545674f1403b41e6fb4d1c1f3979d..124a55667f1848191aae837a3f879d6abb96054b 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,7 +1,7 @@ package de.tudresden.inf.mci.brailleplot.configparser; -import de.tudresden.inf.mci.brailleplot.GeneralResource; +import de.tudresden.inf.mci.brailleplot.util.GeneralResource; import de.tudresden.inf.mci.brailleplot.printerbackend.PrinterCapability; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Image.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Image.java index 1bbc3ca7a66d4931b8259bba2ee85a2cfb1f2745..e489060642715ed344c0ef205301f1a74c85a252 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Image.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Image.java @@ -3,13 +3,15 @@ package de.tudresden.inf.mci.brailleplot.rendering; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; +import java.io.IOException; +import java.net.URL; import java.util.Objects; /** * A representation of an (raster graphic) image. Basically just a wrapper for {@link javax.imageio.ImageIO} and * {@link java.awt.image.BufferedImage}. - * @author Leonard Kupper - * @version 2019.07.22 + * @author Leonard Kupper, Georg Graßnick + * @version 2019.09.24 */ public class Image implements Renderable { @@ -24,6 +26,15 @@ public class Image implements Renderable { imageData = ImageIO.read(Objects.requireNonNull(imageFile)); } + /** + * Constructor. Creates a new renderable representation from an image identified by an URL. + * @param url The URL to a resource containing an raster graphic image. (Different types supported. BMP, PNG, JPEG, ...) + * @throws java.io.IOException If an I/O exception of some sort has occurred while reading the image file. + */ + public Image(final URL url) throws IOException { + imageData = ImageIO.read(url); + } + /** * Get the loaded image as {@link BufferedImage}. * @return An instance of {@link BufferedImage}. 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 0eadd35bd34b23fc5bc408fd0ec4377d4d0380cf..d47dec2bdf145074670ad4b3bdb0394ef4b39947 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,6 +1,6 @@ package de.tudresden.inf.mci.brailleplot.rendering; -import de.tudresden.inf.mci.brailleplot.GeneralResource; +import de.tudresden.inf.mci.brailleplot.util.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; diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/GeneralResource.java b/src/main/java/de/tudresden/inf/mci/brailleplot/util/GeneralResource.java similarity index 78% rename from src/main/java/de/tudresden/inf/mci/brailleplot/GeneralResource.java rename to src/main/java/de/tudresden/inf/mci/brailleplot/util/GeneralResource.java index 3f6c3e6e2f825e85440ce2a46cdf3e9ba2c87298..9ff63c0cdf4bbd20520ab2e02aee0ac5c062fb63 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/GeneralResource.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/util/GeneralResource.java @@ -1,4 +1,4 @@ -package de.tudresden.inf.mci.brailleplot; +package de.tudresden.inf.mci.brailleplot.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,9 +18,9 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; /** - * This class is used to make files & resources available across the app independent from the application running as packed jar or not. - * @author Leonard Kupper - * @version 2019-09-11 + * This class is used to make files and resources available across the app independent from the application running as packed jar or not. + * @author Leonard Kupper, Georg Graßnick + * @version 2019-09-24 */ public final class GeneralResource { @@ -48,14 +48,14 @@ public final class GeneralResource { */ public GeneralResource(final String resourcePath, final String searchPath) throws IOException { File checkFile = new File(resourcePath); - mLogger.trace("Checking referenced path: " + checkFile); + mLogger.debug("Checking referenced path: " + checkFile); if (checkFile.isFile()) { mLogger.trace("Interpreting path as file: " + checkFile.getCanonicalPath()); mResourcePath = checkFile.getCanonicalPath(); mValidExternalFile = true; } checkFile = checkFile.getAbsoluteFile(); - mLogger.trace("Checking referenced path as absolute path: " + checkFile); + mLogger.debug("Checking referenced path as absolute path: " + checkFile); if (checkFile.isFile()) { mLogger.trace("Interpreting path as absolute file: " + checkFile.getCanonicalPath()); mResourcePath = checkFile.getCanonicalPath(); @@ -63,26 +63,31 @@ public final class GeneralResource { } if (Objects.nonNull(searchPath)) { checkFile = new File(searchPath + File.separator + resourcePath); - mLogger.trace("Looking for referenced path in search path: " + checkFile); + mLogger.debug("Looking for referenced path in search path: " + checkFile); if (checkFile.isFile()) { mLogger.trace("Interpreting path as search path relative file: " + checkFile.getCanonicalPath()); mResourcePath = checkFile.getCanonicalPath(); mValidExternalFile = true; } } - String resourceClassPath = resourcePath.replace("\\", "/"); // classpaths are always separated by forward slash - InputStream checkStream = getClass().getResourceAsStream(resourceClassPath); - mLogger.trace("Checking referenced path as resource: " + resourceClassPath); + String resourceSearchPath = searchPath; + if (resourceSearchPath != null) { + resourceSearchPath = stripJarPath(resourceSearchPath); + } + String resourceClassPath = stripJarPath(resourcePath); + resourceClassPath = resourceClassPath.replace(File.separator, "/"); // class paths are always separated by forward slash + InputStream checkStream = getClass().getClassLoader().getResourceAsStream(resourceClassPath); + mLogger.debug("Checking referenced path as resource: " + resourceClassPath); if (Objects.nonNull(checkStream)) { mLogger.trace("Interpreting path as resource stream: " + resourceClassPath); mResourcePath = resourceClassPath; mValidPackedResource = true; } - if (Objects.nonNull(searchPath)) { - String relativeResourcePath = new File(searchPath + File.separator + resourceClassPath).toPath().normalize().toString(); + if (Objects.nonNull(resourceSearchPath)) { + String relativeResourcePath = new File(resourceSearchPath + File.separator + resourceClassPath).toPath().normalize().toString(); relativeResourcePath = relativeResourcePath.replace("\\", "/"); - checkStream = getClass().getResourceAsStream(relativeResourcePath); - mLogger.trace("Checking referenced path as search path relative resource: " + relativeResourcePath); + checkStream = getClass().getClassLoader().getResourceAsStream(relativeResourcePath); + mLogger.debug("Checking referenced path as search path relative resource: " + relativeResourcePath); if (Objects.nonNull(checkStream)) { mLogger.trace("Interpreting path as resource stream: " + relativeResourcePath); mResourcePath = relativeResourcePath; @@ -111,7 +116,7 @@ public final class GeneralResource { if (isValidExternalFile()) { return new FileInputStream(getResourcePath()); } - return getClass().getResourceAsStream(mResourcePath); + return getClass().getClassLoader().getResourceAsStream(mResourcePath); } catch (Exception e) { throw new RuntimeException("Error while opening resource as stream: ", e); } @@ -197,4 +202,20 @@ public final class GeneralResource { private static Class getClassRef() { return GeneralResource.class; } + + /** + * Removes the the jar file path from the given path. + * Example: file:/home/user/BraillePlot.jar!/config/example.properties -> config/example.properties. + * Note that this method identifies the distinction between jar path and inner path by the '!' (exclamation mark) character. Please + * make sure your packaged resource does not contain this character in it's name or path. If you don't, things will break. + * @param path The string representation of the path to use. + * @return The input path where any leading path outside of the jar file is truncated. + */ + private static String stripJarPath(final String path) { + String ret = path.substring(path.lastIndexOf('!') + 1); + if (ret.startsWith("/")) { + ret = ret.substring(1); + } + return ret; + } } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/util/package-info.java b/src/main/java/de/tudresden/inf/mci/brailleplot/util/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..0a568e49d338b9e5031c5514da161a5ef5e28b31 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/util/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains utility classes which can be invoked from all other packages. + */ +package de.tudresden.inf.mci.brailleplot.util; diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParserTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParserTest.java index fe01525acc5b33c9cadd6f917f3c7c82cb5fa8b9..ce71eb080ebfbf12987943715e93386c8c4a6c30 100644 --- a/src/test/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParserTest.java +++ b/src/test/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationParserTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import java.io.File; +import java.net.URL; import java.util.Arrays; import java.util.HashSet; import java.util.NoSuchElementException; @@ -13,15 +13,13 @@ import java.util.Set; public class JavaPropertiesConfigurationParserTest { - public static final String mDefaultConfigPath = getResource("config/default.properties").getAbsolutePath(); - public static final String mConcreteConfigPath = getResource("config/concrete.properties").getAbsolutePath(); + public static final URL mDefaultConfigPath = getResource("config/default.properties"); + public static final URL mConcreteConfigPath = getResource("config/concrete.properties"); public static Printer mPrinterConfig; public static Format mFormatConfig; - public static File getResource(String fileName) { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - File resourceFile = new File(classLoader.getResource(fileName).getFile()); - return resourceFile; + public static URL getResource(final String location) { + return ClassLoader.getSystemClassLoader().getResource(location); } // Correct use testcases @@ -79,13 +77,13 @@ public class JavaPropertiesConfigurationParserTest { @Test public void testIllegalFile() { Assertions.assertThrows( - ConfigurationParsingException.class, - () -> new JavaPropertiesConfigurationParser("config/nonexistent.properties", mDefaultConfigPath) + NullPointerException.class, + () -> new JavaPropertiesConfigurationParser(getResource("config/nonexistent.properties"), mDefaultConfigPath) ); } @Test public void testMissingRequired() { - String configPath = getResource("config/missingRequiredPropertyExample.properties").getAbsolutePath(); + URL configPath = getResource("config/missingRequiredPropertyExample.properties"); Assertions.assertThrows( IllegalStateException.class, () -> new JavaPropertiesConfigurationParser(configPath, mDefaultConfigPath) @@ -93,7 +91,7 @@ public class JavaPropertiesConfigurationParserTest { } @Test public void testIllegalProperty() { - String configPath = getResource("config/illegalPropertyNameExample.properties").getAbsolutePath(); + URL configPath = getResource("config/illegalPropertyNameExample.properties"); Assertions.assertThrows( ConfigurationValidationException.class, () -> new JavaPropertiesConfigurationParser(configPath, mDefaultConfigPath) @@ -101,7 +99,7 @@ public class JavaPropertiesConfigurationParserTest { } @Test public void testIllegalValue() { - String configPath = getResource("config/illegalPropertyValueExample.properties").getAbsolutePath(); + URL configPath = getResource("config/illegalPropertyValueExample.properties"); Assertions.assertThrows( ConfigurationValidationException.class, () -> new JavaPropertiesConfigurationParser(configPath, mDefaultConfigPath) diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/layout/RasterCanvasTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/layout/RasterCanvasTest.java index f0cbc05ce550ce78ee1261c77801fb15da0c952c..5401330467bd48923bdea75abf543f27688340c3 100644 --- a/src/test/java/de/tudresden/inf/mci/brailleplot/layout/RasterCanvasTest.java +++ b/src/test/java/de/tudresden/inf/mci/brailleplot/layout/RasterCanvasTest.java @@ -2,9 +2,6 @@ package de.tudresden.inf.mci.brailleplot.layout; import de.tudresden.inf.mci.brailleplot.configparser.ConfigurationParser; import de.tudresden.inf.mci.brailleplot.configparser.JavaPropertiesConfigurationParser; -import de.tudresden.inf.mci.brailleplot.layout.RasterCanvas; -import de.tudresden.inf.mci.brailleplot.layout.Rectangle; -import de.tudresden.inf.mci.brailleplot.layout.SixDotBrailleRasterCanvas; import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData; import de.tudresden.inf.mci.brailleplot.rendering.Image; import de.tudresden.inf.mci.brailleplot.rendering.MasterRenderer; @@ -12,21 +9,20 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.File; +import java.net.URL; import java.util.ListIterator; public class RasterCanvasTest { - public static final String mDefaultConfig = getResource("config/rasterizer_test_default.properties").getAbsolutePath(); - public static final String mBaseConfig = getResource("config/base_format.properties").getAbsolutePath(); - public static final String mMarginsOnlyConfig = getResource("config/margins_only.properties").getAbsolutePath(); - public static final String mConstraintOnlyConfig = getResource("config/constraint_only.properties").getAbsolutePath(); - public static final String mBothConfig = getResource("config/margins_and_constraint.properties").getAbsolutePath(); + public static final URL mDefaultConfig = getResource("config/rasterizer_test_default.properties"); + public static final URL mBaseConfig = getResource("config/base_format.properties"); + public static final URL mMarginsOnlyConfig = getResource("config/margins_only.properties"); + public static final URL mConstraintOnlyConfig = getResource("config/constraint_only.properties"); + public static final URL mBothConfig = getResource("config/margins_and_constraint.properties"); - public static File getResource(String fileName) { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - File resourceFile = new File(classLoader.getResource(fileName).getFile()); - return resourceFile; + public static URL getResource(final String location) { + return ClassLoader.getSystemClassLoader().getResource(location); } @Test diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizerTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizerTest.java index 630419f330bc118d71d6add1be0aca3d023d82e3..0a410a48602d8a1272023ed4f01898a9c490a4ff 100644 --- a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizerTest.java +++ b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/FunctionalRasterizerTest.java @@ -11,19 +11,18 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.File; +import java.net.URL; public class FunctionalRasterizerTest { - public static final String mDefaultConfig = getResource("config/rasterizer_test_default.properties").getAbsolutePath(); - public static final String mBaseConfig = getResource("config/base_format.properties").getAbsolutePath(); + public static final URL mDefaultConfig = getResource("config/rasterizer_test_default.properties"); + public static final URL mBaseConfig = getResource("config/base_format.properties"); public static Printer mPrinter; public static Format mFormat; - public static File getResource(String fileName) { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - File resourceFile = new File(classLoader.getResource(fileName).getFile()); - return resourceFile; + public static URL getResource(final String location) { + return ClassLoader.getSystemClassLoader().getResource(location); } @BeforeAll diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRendererTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRendererTest.java index 27a0db664751722ae32a3d1bdee76a04a745e46c..cbd630e1ac20fde76502301d57f9207a8a440a6f 100644 --- a/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRendererTest.java +++ b/src/test/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRendererTest.java @@ -10,20 +10,18 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import java.io.File; +import java.net.URL; public class MasterRendererTest { - public static final String mDefaultConfig = getResource("config/rasterizer_test_default.properties").getAbsolutePath(); - public static final String mBaseConfig = getResource("config/base_format.properties").getAbsolutePath(); + public static final URL mDefaultConfig = getResource("config/rasterizer_test_default.properties"); + public static final URL mBaseConfig = getResource("config/base_format.properties"); public static Printer mPrinter; public static Format mFormat; - public static File getResource(String fileName) { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - File resourceFile = new File(classLoader.getResource(fileName).getFile()); - return resourceFile; + public static URL getResource(final String location) { + return ClassLoader.getSystemClassLoader().getResource(location); } @BeforeAll