From b19a3d47f08080677f0b1ad267f20bec1c553ead Mon Sep 17 00:00:00 2001 From: Leonard Kupper <leonard.kupper@mailbox.tu-dresden.de> Date: Wed, 21 Aug 2019 12:02:26 +0200 Subject: [PATCH] Restructure config parser to handle internal recursive calls for include files. --- .../configparser/ConfigurationParser.java | 49 ++++++++------- .../JavaPropertiesConfigurationParser.java | 59 +++++++++++++++---- 2 files changed, 78 insertions(+), 30 deletions(-) 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 4eeaccb9..49872d3a 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 @@ -19,11 +19,10 @@ import java.util.Set; public abstract class ConfigurationParser { - private FileInputStream mInput; private ConfigurationValidator mValidator; private Printer mPrinter; private Map<String, Format> mFormats = new HashMap<>(); - private String mConfigFilePath; + private File mCurrentConfigFile; private List<PrinterProperty> mPrinterProperties = new ArrayList<>(); private Map<String, List<FormatProperty>> mFormatProperties = new HashMap<>(); private Printer mDefaultPrinter; @@ -35,14 +34,15 @@ public abstract class ConfigurationParser { /** * Internal algorithm used for parsing of the configuration file. - * Implement this method by parsing information from the configuration file (see {@link #getInput()}), optionally validating it (see {@link ConfigurationValidator}), + * 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. * @throws ConfigurationValidationException On any error while checking the parsed properties validity. */ - protected abstract void parse() throws ConfigurationParsingException, ConfigurationValidationException; + protected abstract void parse(FileInputStream input) throws ConfigurationParsingException, ConfigurationValidationException; /** @@ -50,7 +50,7 @@ public abstract class ConfigurationParser { * @return A {@link File} object representing the configuration file. */ public final File getConfigFile() { - return new File(mConfigFilePath); + return mCurrentConfigFile; } /** @@ -131,14 +131,6 @@ public abstract class ConfigurationParser { mDefaultFormat = defaultFormat; } - /** - * Get the input data from the current configuration file. - * @return A {@link FileInputStream} from the current configuration file. - */ - protected FileInputStream getInput() { - return mInput; - } - /** * Parse the specified configuration file. * This method should be called inside the concrete parsers constructor after the optional default configurations @@ -154,8 +146,10 @@ public abstract class ConfigurationParser { mPrinterProperties.clear(); mFormatProperties.clear(); // load and parse file - setConfigFile(filePath); - parse(); + mCurrentConfigFile = new File(filePath); + FileInputStream input = openInputStream(filePath); + parse(input); + closeInputStream(input); // build printer object from added properties mPrinter = new Printer(mPrinterProperties); if (mDefaultPrinter != null) { @@ -177,15 +171,30 @@ public abstract class ConfigurationParser { } } - - - private void setConfigFile(final String filePath) throws ConfigurationParsingException { - mConfigFilePath = filePath; + /** + * 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) + */ + final FileInputStream openInputStream(final String filePath) throws ConfigurationParsingException { try { - mInput = new FileInputStream(mConfigFilePath); + return new FileInputStream(filePath); } catch (IOException e) { throw new ConfigurationParsingException("Unable to read configuration file", e); } } + /** + * Closes the given input stream. + * @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 { + try { + input.close(); + } catch (IOException e) { + throw new ConfigurationParsingException("Unable to close input stream.", 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 577fa325..df66792b 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,7 +1,10 @@ package de.tudresden.inf.mci.brailleplot.configparser; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; +import java.util.Stack; /** * Concrete parser for configuration files in Java Property File format. @@ -10,7 +13,8 @@ import java.util.Properties; */ public final class JavaPropertiesConfigurationParser extends ConfigurationParser { - private Properties mProperties = new Properties(); + //ArrayList<String> mInclusionStack; + Stack<File> mInclusionStack = new Stack<>(); /** * Constructor. @@ -33,23 +37,29 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser /** * Concrete internal algorithm used for parsing the Java Property File. - * This method is called by ({@link #parseConfigFile(String, boolean)}). + * 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. * @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() throws ConfigurationParsingException, ConfigurationValidationException { - // Load properties from the .properties file + protected void parse(final FileInputStream input) throws ConfigurationParsingException, ConfigurationValidationException { + // Create property instance for current recursion level + Properties properties = new Properties(); try { - // Reset java property instance - mProperties.clear(); - mProperties.load(getInput()); + // Load properties from the .properties file + properties.load(input); } catch (IOException e) { throw new ConfigurationParsingException("Unable to load properties from file.", e); } // Iterate over all properties as key -> value pairs - for (String key : mProperties.stringPropertyNames()) { - String value = mProperties.getProperty(key); - parseProperty(key, value); + for (String key : properties.stringPropertyNames()) { + String value = properties.getProperty(key); + // check for special property key: 'include' + if (("include").equals(key.toLowerCase())) { + includeFiles(value); + } else { + parseProperty(key, value); + } } } @@ -62,4 +72,33 @@ public final class JavaPropertiesConfigurationParser extends ConfigurationParser addProperty((PrinterProperty) property); } } + + private void includeFiles(final String fileList) throws ConfigurationParsingException, ConfigurationValidationException { + for (String includeName : fileList.split(",")) { + if (mInclusionStack.empty()) { + mInclusionStack.push(getConfigFile()); + } + File includeFile, parentFile = mInclusionStack.peek().getParentFile(); + 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(); + } catch (IOException e) { + throw new ConfigurationParsingException("Can not find include file.", e); + } + if (mInclusionStack.contains(includeFile)) { + System.out.println("Skipping " + includeFile); + continue; + } + FileInputStream includeInput = openInputStream(includeFile.getAbsolutePath()); + mInclusionStack.push(includeFile); + System.out.println("Including " + mInclusionStack); + parse(includeInput); + mInclusionStack.pop(); + closeInputStream(includeInput); + } + } } -- GitLab