diff --git a/build.gradle b/build.gradle index 8df5ccb63b5db4ebbb6e41084eb38cd7dce409ef..546399c7042496a136092619acedc688de441986 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,7 @@ repositories { dependencies { // Use JUnit test framework + compile group: 'commons-cli', name: 'commons-cli', version: '1.4' testImplementation('org.junit.jupiter:junit-jupiter:5.4.2') // Logging 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 023513bc72da8ed83ffaf7c3c5d67ac1c6319f7f..6b0a9f5c1ddaa5b4e5922abb1d935998bf0286ea 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java @@ -1,8 +1,14 @@ package de.tudresden.inf.mci.brailleplot; -import de.tudresden.inf.mci.brailleplot.configparser.PrinterProperty; +import de.tudresden.inf.mci.brailleplot.commandline.CommandLineParser; +import de.tudresden.inf.mci.brailleplot.commandline.SettingType; +import de.tudresden.inf.mci.brailleplot.commandline.SettingsReader; +import de.tudresden.inf.mci.brailleplot.commandline.SettingsWriter; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import java.util.Optional; import java.util.concurrent.ConcurrentLinkedDeque; /** @@ -107,10 +113,17 @@ public final class App { mLogger.info("Application started"); // Parse command line parameters + CommandLineParser cliParser = new CommandLineParser(); + SettingsWriter settings = cliParser.parse(args); + SettingsReader settingsReader = settings; // If requested, print help and exit - + Optional<Boolean> printHelp = settingsReader.isTrue(SettingType.DISPLAY_HELP); + if (printHelp.isPresent() && printHelp.get()) { + cliParser.printHelp(); + return EXIT_SUCCESS; + } // Parse csv data diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java new file mode 100644 index 0000000000000000000000000000000000000000..f36ad63a08d171d1946f96a29cc05908c561c89f --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java @@ -0,0 +1,58 @@ +package de.tudresden.inf.mci.brailleplot.commandline; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** + * Performs command line parsing and creates a {@link Settings} object. + * @author Georg GraĆnick + * @version 2019.05.31 + */ +public class CommandLineParser { + + private Options mOptions; + + public CommandLineParser() { + setupOptions(); + } + + private void setupOptions() { + mOptions = new Options(); + mOptions.addOption("h", SettingType.DISPLAY_HELP.toString(), false, "Print help and exit") + .addOption("c", SettingType.CSV_LOCATION.toString(), true, "Path to CSV") + .addOption("s", SettingType.SEMANTIC_MAPPING.toString(), true, "Literal for semantic mapping") + .addOption("p", SettingType.PRINTER_CONFIG_PATH.toString(), true, "path to printer configuration file"); + } + + /** + * Parse command line parameters. + * @param args The arguments from the commandline. + * @return A {@link Settings} object that represents the values from the command line parameters. + * @throws ParsingException On any underlying error. + */ + public final Settings parse(final String[] args) throws ParsingException { + + org.apache.commons.cli.CommandLineParser parser = new DefaultParser(); + CommandLine cmdLine; + try { + cmdLine = parser.parse(mOptions, args); + } catch (ParseException pe) { + throw new ParsingException("Could not parse command line", pe); + } + return new Settings(cmdLine); + } + + /** + * Print usage information to the command line. + */ + public final void printHelp() { + HelpFormatter formatter = new HelpFormatter(); + String headerForOptions = "Convert csv into braille"; + String footerForOptions = "Report Issues to Leonard Kupper"; + + formatter.printHelp("braillegraphics", headerForOptions, mOptions, footerForOptions, true); + } +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/ParsingException.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/ParsingException.java new file mode 100644 index 0000000000000000000000000000000000000000..cd1a7e9ed9e59763ac3586662525ff057d72247b --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/ParsingException.java @@ -0,0 +1,25 @@ +package de.tudresden.inf.mci.brailleplot.commandline; + +/** + * Exception class. + * Indicates, some error has occurred while parsing the command line parameters. + * Most likely created by malformed user input. + * @author Georg GraĆnick + * @version 2019.05.31 + */ +public class ParsingException extends Exception { + + public ParsingException() { } + + public ParsingException(final String message) { + super(message); + } + + public ParsingException(final Throwable cause) { + super(cause); + } + + public ParsingException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java new file mode 100644 index 0000000000000000000000000000000000000000..010c9c528891ffdb81549f715eac363625469e76 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java @@ -0,0 +1,39 @@ +package de.tudresden.inf.mci.brailleplot.commandline; + +/** + * Represents all possible parsed options parsed from the command line. + * @author Georg GraĆnick + * @version 2019.05.31 + */ +public enum SettingType { + + DISPLAY_HELP("help"), + CSV_LOCATION("csv-path"), + PRINTER_CONFIG_PATH("printer-config-path"), + SEMANTIC_MAPPING("semantic-mapping"); + + private final String mName; + + SettingType(final String name) { + this.mName = name; + } + + static SettingType fromString(final String s) { + switch (s) { + case "help": + return DISPLAY_HELP; + case "csv-path": + return CSV_LOCATION; + case "printer-config-path": + return PRINTER_CONFIG_PATH; + case "semantic-mapping": + return SEMANTIC_MAPPING; + default: + throw new IllegalArgumentException("Setting not available"); + } + } + + public String toString() { + return mName; + } +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/Settings.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/Settings.java new file mode 100644 index 0000000000000000000000000000000000000000..7cd4eee3f819751758e1bfb00021dcd53adbc154 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/Settings.java @@ -0,0 +1,126 @@ +package de.tudresden.inf.mci.brailleplot.commandline; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; + +/** + * Encapsulates Settings for the Application from the command line. + * @author Georg GraĆnick + * @version 2019.05.31 + */ +public final class Settings implements SettingsReader, SettingsWriter { + + private Map<SettingType, String> mSettings; + + private final Logger mLogger = LoggerFactory.getLogger(this.getClass()); + + private static final String BOOL_TRUE = Boolean.toString(true); + private static final String BOOL_FALSE = Boolean.toString(false); + + /** + * Constructor. + * @param cmdLine CommandLine object to take information from. + */ + Settings(final CommandLine cmdLine) { + mSettings = new HashMap<>(); + setup(cmdLine); + } + + + /** + * Populates the Settings class with arguments from the command line. + * @param cmdLine {@link CommandLine} object to take take settings from. + */ + private void setup(final CommandLine cmdLine) { + for (Iterator<Option> it = cmdLine.iterator(); it.hasNext();) { + Option o = it.next(); + if (o.hasArg()) { + SettingType type = SettingType.fromString(o.getLongOpt()); + mSettings.put(type, o.getValue()); + mLogger.trace("Added string \"{}\" with value \"{}\"", type, o.getValue()); + } else { + mSettings.put(SettingType.fromString(o.getLongOpt()), BOOL_TRUE); + mLogger.trace("Added flag \"{}\" set to \"{}\"", SettingType.fromString(o.getLongOpt()), BOOL_TRUE); + } + } + } + + /** + * Check if a specific setting is set. + * @param setting The requested setting. + * @return true if the parameter is set, else false. + */ + public boolean isPresent(final SettingType setting) { + return mSettings.containsKey(setting); + } + + /** + * Get the value of a setting, if set. + * @param setting The requested Setting to request the value for. + * @return {@link Optional}{@literal <String>} if setting is set, or {@link Optional#empty()} if the requested setting + * was not specified by the user. + * Use {@link Settings#isPresent(SettingType)} to check the value of a boolean parameter. + */ + public Optional<String> getSetting(final SettingType setting) { + if (!mSettings.containsKey(setting)) { + return Optional.empty(); + } + return Optional.of(mSettings.get(setting)); + } + + /** + * Get the boolean value of a setting, if set. + * @param setting The requested Setting to request the value for. + * @return {@link Optional}{@literal <Boolean>} if setting is set, or {@link Optional#empty()} if the requested setting + * was not specified by the user. + */ + public Optional<Boolean> isTrue(final SettingType setting) { + if (!mSettings.containsKey(setting)) { + return Optional.empty(); + } + + if (mSettings.get(setting).equals(BOOL_TRUE)) { + return Optional.of(true); + } else { + return Optional.of(false); + } + } + + /** + * Set the value of a setting. + * @param setting {@link SettingType} the setting to be changed. + * @param value {@link String} The value to set. + */ + public void setSetting(final SettingType setting, final String value) { + mSettings.replace(setting, value); + } + + /** + * Set the value of a setting to a specific {@link Boolean}. + * @param setting {@link SettingType} the setting to be changed. + * @param isTrue {@link String} The value to set. + */ + public void setSetting(final SettingType setting, final Boolean isTrue) { + if (isTrue) { + mSettings.replace(setting, BOOL_TRUE); + } else { + mSettings.replace(setting, BOOL_FALSE); + } + } + + /** + * Delete the set value of a setting, if it is set. + * @param setting {@link SettingType} the setting to be removed. + */ + public void deleteSettingValue(final SettingType setting) { + mSettings.remove(setting); + } + +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingsReader.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingsReader.java new file mode 100644 index 0000000000000000000000000000000000000000..3592e2c8ebbbd4b3edee8ab04d44b299efa66d33 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingsReader.java @@ -0,0 +1,15 @@ +package de.tudresden.inf.mci.brailleplot.commandline; + +import java.util.Optional; + +/** + * Supplies an interface for reading settings. + * @author Georg GraĆnick + * @version 2019.06.01 + */ +public interface SettingsReader { + + Optional<String> getSetting(SettingType setting); + boolean isPresent(SettingType setting); + Optional<Boolean> isTrue(SettingType setting); +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingsWriter.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingsWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..2aa534fa903a46d17f0c6684b5771a8b14f640cb --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingsWriter.java @@ -0,0 +1,14 @@ +package de.tudresden.inf.mci.brailleplot.commandline; + +/** + * Implementing classes are guaranteed to support manipulating settings. + * Currently, the implementation is not thread safe! + * @author Georg GraĆnick + * @version 2019.06.01 + */ +public interface SettingsWriter extends SettingsReader { + + void setSetting(SettingType setting, String value); + void setSetting(SettingType setting, Boolean isTrue); + void deleteSettingValue(SettingType setting); +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/package-info.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..eff04978158a1750884682d02ac72e3f74c45628 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/package-info.java @@ -0,0 +1,6 @@ +/** + * Responsible for command line parsing and the creation of an {@link de.tudresden.inf.mci.brailleplot.commandline.Settings} object + * which can be queried by {@link de.tudresden.inf.mci.brailleplot.commandline.SettingsReader} + * and manipulated by {@link de.tudresden.inf.mci.brailleplot.commandline.SettingsWriter}. + */ +package de.tudresden.inf.mci.brailleplot.commandline; diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParserTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..436ef4d2be887c6df4d423c42497c91b68df58df --- /dev/null +++ b/src/test/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParserTest.java @@ -0,0 +1,94 @@ +package de.tudresden.inf.mci.brailleplot.commandline; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Tests the CommandlineParser and the querying of parameters of the Settings object. + */ +class CommandLineParserTest { + + private CommandLineParser commandLineParser = new CommandLineParser(); + + @Test + void testParseLegalArgs() { + String[] args = {"-h", "--csv-path", "foobar"}; + Assertions.assertDoesNotThrow(() -> {commandLineParser.parse(args);}); + } + + @Test + void testIllegalArgs() { + String[] args = {"-h --foobar"}; + Assertions.assertThrows(ParsingException.class, () -> {commandLineParser.parse(args);}); + } + + @Test + void testEmptyArgs() { + String[] args = {""}; + Assertions.assertDoesNotThrow(() -> {commandLineParser.parse(args);}); + } + + @Test + void testBoolFlagRecognized() { + String[] args = {"-h"}; + SettingsReader settings; + try { + settings = commandLineParser.parse(args); + } catch (ParsingException pe) { + fail(); + return; // Never executed, satisfy compiler + } + Optional<Boolean> flag = settings.isTrue(SettingType.DISPLAY_HELP); + Assertions.assertTrue(flag.isPresent()); + Assertions.assertTrue(flag.get()); + } + + @Test + void testBoolFlagCorrectlyNotRecognized() { + String[] args = {""}; + SettingsReader settings; + try { + settings = commandLineParser.parse(args); + } catch (ParsingException pe) { + fail(); + return; // Never executed, satisfy compiler + } + Optional<Boolean> flag = settings.isTrue(SettingType.DISPLAY_HELP); + Assertions.assertFalse(flag.isPresent()); + } + + @Test + void testParameterRecognized() { + final String param = "xyz"; + String[] args = {"--csv-path", param}; + SettingsReader settings; + try { + settings = commandLineParser.parse(args); + } catch (ParsingException pe) { + fail(); + return; // Never executed, satisfy compiler + } + Optional<String> flag = settings.getSetting(SettingType.CSV_LOCATION); + Assertions.assertTrue(flag.isPresent()); + Assertions.assertTrue(flag.get().equals(param)); + } + + @Test + void testParameterCorrectlyNotRecognized() { + String[] args = {""}; + SettingsReader settings; + try { + settings = commandLineParser.parse(args); + } catch (ParsingException pe) { + fail(); + return; // Never executed, satisfy compiler + } + Optional<String> flag = settings.getSetting(SettingType.CSV_LOCATION); + Assertions.assertFalse(flag.isPresent()); + } +} +