diff --git a/pom.xml b/pom.xml index c4643d0..d3ddf53 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.fross cal - 2.3.18 + 2.4.0 jar cal @@ -179,11 +179,11 @@ - + - gnu.getopt - java-getopt - 1.0.13 + com.beust + jcommander + 1.82 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a830341..186b39c 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: fcal -version: '2.3.18' +version: '2.4.0' summary: Command line calendar display description: | fCal is a command line calendar display utility. It will display a diff --git a/src/main/java/org/fross/cal/Calendar.java b/src/main/java/org/fross/cal/Calendar.java index 29734ef..641bf9e 100644 --- a/src/main/java/org/fross/cal/Calendar.java +++ b/src/main/java/org/fross/cal/Calendar.java @@ -61,6 +61,15 @@ public static void setCalsPerRow(int cpr) { } } + /** + * queryCalsPerRow(): Return the current number of calendars printed per row + * + * @return + */ + public static int queryCalsPerRow() { + return calsPerRow; + } + /** * firstDay(): Given the month, day, and year, return which day of the week it falls * diff --git a/src/main/java/org/fross/cal/CommandLineArgs.java b/src/main/java/org/fross/cal/CommandLineArgs.java new file mode 100644 index 0000000..d9e6698 --- /dev/null +++ b/src/main/java/org/fross/cal/CommandLineArgs.java @@ -0,0 +1,182 @@ +/****************************************************************************** + * Cal - A command line calendar utility + * + * Copyright (c) 2019-2022 Michael Fross + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ******************************************************************************/ +package org.fross.cal; + +import java.util.ArrayList; +import java.util.List; + +import org.fross.library.Debug; +import org.fross.library.GitHub; +import org.fross.library.Output; +import org.fusesource.jansi.Ansi; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; + +public class CommandLineArgs { + static CommandLineArgs cli = new CommandLineArgs(); + static JCommander jc = new JCommander(); + static int monthToUse = org.fross.library.Date.getCurrentMonth(); + static int yearToUse = org.fross.library.Date.getCurrentYear(); + + // --------------------------------------------------------------------------------------------- + // Define command line options that can be used + // --------------------------------------------------------------------------------------------- + + @Parameter(names = { "-h", "-?", "--help" }, help = true, description = "Display cal help and exit") + protected boolean clHelp = false; + + @Parameter(names = { "-z", "--no-color" }, description = "Disable colorized output") + protected boolean clNoColor = false; + + @Parameter(names = { "-v", "--version" }, description = "Show current program version and latest release on GitHub") + protected boolean clVersion = false; + + @Parameter(names = { "-D", "--debug" }, description = "Turn on Debug mode to display extra program information") + protected boolean clDebug = false; + + @Parameter(names = { "-n", "--num" }, description = "Number of calendar months to display per row") + protected int clNum = Calendar.DEFAULT_CALS_PER_ROW; + + @Parameter(description = "Month and/or Year") + protected List clMonthAndOrYear = new ArrayList<>(); + + // --------------------------------------------------------------------------------------------- + // Process command line parameters with the following methods + // --------------------------------------------------------------------------------------------- + public static void ProcessCommandLine(String[] argv) { + // JCommander parses the command line + try { + jc.setProgramName("cal"); + jc = JCommander.newBuilder().addObject(cli).build(); + jc.parse(argv); + } catch (ParameterException ex) { + System.out.println(ex.getMessage()); + jc.usage(); + System.exit(0); + } + + // --------------------------------------------------------------------------------------------- + // Process the parsed command line options + // --------------------------------------------------------------------------------------------- + // Debug Switch + if (cli.clDebug == true) + Debug.enable(); + + // Set the stack name and restore stack from Preferences + if (cli.clNum != Calendar.DEFAULT_CALS_PER_ROW) { + try { + if (cli.clNum <= 0) { + throw new UnsupportedOperationException(); + } + } catch (Exception Ex) { + Output.fatalError("Invalid option for -n switch: '" + cli.clNum + "'", 0); + } + Calendar.setCalsPerRow(cli.clNum); + } + + // Version Switch + if (cli.clVersion == true) { + Output.printColorln(Ansi.Color.WHITE, "Cal Version: v" + Main.VERSION); + Output.printColorln(Ansi.Color.CYAN, Main.COPYRIGHT); + Output.printColorln(Ansi.Color.WHITE, "\nLatest Release on GitHub: " + GitHub.updateCheck("cal")); + Output.printColorln(Ansi.Color.CYAN, "HomePage: https://github.com/frossm/cal"); + System.exit(0); + } + + // Disable Colorized Output Switch + if (cli.clNoColor == true) { + Output.enableColor(false); + } + + // Show Help and Exit + if (cli.clHelp == true) { + Help.display(); + System.exit(0); + } + + // Process any month/year parameters that are given and set monthToUse and yeareToUse + try { + switch (cli.clMonthAndOrYear.size()) { + + // Process no dates provided + case 0: + Output.debugPrint("No Month or Year provided on command line. Showing current year: " + yearToUse); + break; + + // Just a date or month provided + case 1: + int d = Integer.parseInt(cli.clMonthAndOrYear.get(0)); + + // Number must be a year if it's greater than 12 + if (d > 12) { + yearToUse = d; + Output.debugPrint("Commandline Year provided. Showing Year: " + yearToUse); + + // If number is <= 12, assume it's a month + } else { + monthToUse = d; + Output.debugPrint("Commandline Month provided. Using Month: " + monthToUse + " Year:" + yearToUse); + } + break; + + // Month & year provided + case 2: + monthToUse = Integer.parseInt(cli.clMonthAndOrYear.get(0)); + yearToUse = Integer.parseInt(cli.clMonthAndOrYear.get(1)); + Output.debugPrint("Commandline Month & Year provided. Month: " + monthToUse + " Year: " + yearToUse); + break; + } + + } catch (NumberFormatException ex) { + Output.fatalError("Parameters can only be numbers. Usage '-h' for options", 99); + + } catch (Exception ex) { + ex.getMessage(); + ex.printStackTrace(); + Output.fatalError("Something went very wrong. You shouldn't really see this. Eeek!", 99); + } + + } + + /** + * Return the month to use after processing the command line + * + * @return + */ + public static int queryMonthToUse() { + return monthToUse; + } + + /** + * Return the year to use after processing the command line + * + * @return + */ + public static int queryYearToUse() { + return yearToUse; + } +} diff --git a/src/main/java/org/fross/cal/Main.java b/src/main/java/org/fross/cal/Main.java index 9c398ed..5ef1f25 100644 --- a/src/main/java/org/fross/cal/Main.java +++ b/src/main/java/org/fross/cal/Main.java @@ -29,11 +29,7 @@ import java.util.Properties; import org.fross.library.Debug; -import org.fross.library.GitHub; import org.fross.library.Output; -import org.fusesource.jansi.Ansi; - -import gnu.getopt.Getopt; /** * Main - Main program execution class @@ -42,7 +38,6 @@ * */ public class Main { - // Class Constants public static String VERSION; public static String COPYRIGHT; @@ -54,10 +49,6 @@ public class Main { * @param args */ public static void main(String[] args) { - int optionEntry; - int month, year; - int numCalPerRow = 0; - // Process application level properties file // Update properties from Maven at build time: // https://stackoverflow.com/questions/3697449/retrieve-version-from-maven-pom-xml-in-code @@ -71,122 +62,59 @@ public static void main(String[] args) { Output.fatalError("Unable to read property file '" + PROPERTIES_FILE + "'", 3); } - // Populate the month and year with todays values as a default - month = org.fross.library.Date.getCurrentMonth(); - year = org.fross.library.Date.getCurrentYear(); - - // Process Command Line Options and set flags where needed - Getopt optG = new Getopt("cal", args, "n:Dvzh?"); - while ((optionEntry = optG.getopt()) != -1) { - switch (optionEntry) { - case 'n': // Set Number of Calendars per Row - try { - numCalPerRow = Integer.parseInt(optG.getOptarg()); - if (numCalPerRow <= 0) { - throw new UnsupportedOperationException(); - } - } catch (Exception Ex) { - Output.fatalError("Invalid option for -n switch: '" + optG.getOptarg() + "'", 0); - } - Calendar.setCalsPerRow(numCalPerRow); - break; - - case 'D': // Debug Mode - Debug.enable(); - break; - - case 'v': // Version - Output.printColorln(Ansi.Color.WHITE, "Cal Version: v" + VERSION); - Output.printColorln(Ansi.Color.CYAN, COPYRIGHT); - Output.printColorln(Ansi.Color.WHITE, "\nLatest Release on GitHub: " + GitHub.updateCheck("cal")); - Output.printColorln(Ansi.Color.CYAN, "HomePage: https://github.com/frossm/cal"); - System.exit(0); - break; - - case 'z': // Disable Colorized Output - Output.enableColor(false); - break; - - case '?': // Help - case 'h': - Help.display(); - System.exit(0); - break; - - default: - Output.fatalError("ERROR: Unknown Command Line Option -" + optG.getOptarg() + "'", 0); - Help.display(); - break; - } - } + // Process the command line arguments and switches + CommandLineArgs.ProcessCommandLine(args); // Display some useful information about the environment if in Debug Mode Debug.displaySysInfo(); Output.debugPrint("Command Line Options"); Output.debugPrint(" -D: " + Debug.query()); - Output.debugPrint(" -n: " + numCalPerRow); - - // Process the command line parameters (non-options). Update month and year as needed + Output.debugPrint(" -n: " + Calendar.queryCalsPerRow()); + Output.debugPrint(" -z: " + Output.queryColorEnabled()); Output.debugPrint("Number of command line arguments: " + args.length); - int clParameters = args.length - optG.getOptind(); - Output.debugPrint("Number of command line parameters: " + clParameters); - Output.debugPrint("Current Date: Month: " + month + " Year: " + year); - - // Ensure the month and year are greater than zero - for (int i = 0; i < clParameters; i++) { - if (args[i].compareTo("0") == 0) { - Output.fatalError("Month & Year must be greater than zero", 6); - } + + // Ensure there are not more than 2 parameters given + if (CommandLineArgs.cli.clMonthAndOrYear.size() > 2) { + Output.fatalError("There can not be more than 2 dates given on the commandline.\nPlease see Help (-h)", 6); } - // Process options and display the calendar - try { - Output.println(""); - - switch (clParameters) { - case 0: - // Process no dates provided - Output.debugPrint("No Month or Year provided on command line. Using Year: " + year); - Calendar.printYear(month, year); - break; - - case 1: - // Just a date or month provided - int d = Integer.parseInt(args[optG.getOptind()]); - - // Assume the number provided is a year - if (d > 12) { - year = d; - Output.debugPrint("Commandline Year provided. Using Month: " + month + " Year: " + year); - Calendar.printYear(month, year); - - // Assume the number provided is a month - } else { - month = d; - Output.debugPrint("Commandline Month provided. Using Month: " + month + " Year:" + year); - Calendar.printMonth(month, year); + // Ensure the month / year is a valid integer + for (int i = 0; i < CommandLineArgs.cli.clMonthAndOrYear.size(); i++) { + int monthAndOrYear = 0; + try { + monthAndOrYear = Integer.parseInt(CommandLineArgs.cli.clMonthAndOrYear.get(i)); + + // Ensure no negative value is provided for the month and/or year + if (monthAndOrYear <= 0) { + Output.fatalError("Month & Year values must be greater than zero", 6); } - break; - case 2: - // Month & year provided - month = Integer.parseInt(args[optG.getOptind()]); - year = Integer.parseInt(args[optG.getOptind() + 1]); - Output.debugPrint("Commandline Month & Year provided. Month: " + month + " Year: " + year); - Calendar.printMonth(month, year); - break; - - default: - // Ignore anything beyond the first two parameters - break; + + } catch (Exception ex) { + Output.fatalError("Invalid Month and/or Year: '" + monthAndOrYear + "'", 6); + } + } + + // Display the calendars + Output.println(""); + switch (CommandLineArgs.cli.clMonthAndOrYear.size()) { + case 0: + Calendar.printYear(CommandLineArgs.queryMonthToUse(), CommandLineArgs.queryYearToUse()); + break; + + case 1: + if (Integer.parseInt(CommandLineArgs.cli.clMonthAndOrYear.get(0)) > 12) { + Calendar.printYear(CommandLineArgs.queryMonthToUse(), CommandLineArgs.queryYearToUse()); + } else { + Calendar.printMonth(CommandLineArgs.queryMonthToUse(), CommandLineArgs.queryYearToUse()); } + break; + + case 2: + Calendar.printMonth(CommandLineArgs.queryMonthToUse(), CommandLineArgs.queryYearToUse()); + break; - } catch (NumberFormatException ex) { - Output.fatalError("Parameters can only be numbers. Usage '-h' for options", 0); - } catch (Exception ex) { - ex.getMessage(); - ex.printStackTrace(); - Output.fatalError("Something went very wrong. You shouldn't really see this. Eeek!", 0); } } + } \ No newline at end of file diff --git a/src/test/java/org/fross/cal/CalendarTest.java b/src/test/java/org/fross/cal/CalendarTest.java index 2db8c79..cce0f00 100644 --- a/src/test/java/org/fross/cal/CalendarTest.java +++ b/src/test/java/org/fross/cal/CalendarTest.java @@ -1,6 +1,27 @@ -/** - * - */ +/****************************************************************************** + * Cal - A command line calendar utility + * + * Copyright (c) 2019-2022 Michael Fross + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ******************************************************************************/ package org.fross.cal; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/org/fross/cal/CommandLineArgsTest.java b/src/test/java/org/fross/cal/CommandLineArgsTest.java new file mode 100644 index 0000000..48d9e0e --- /dev/null +++ b/src/test/java/org/fross/cal/CommandLineArgsTest.java @@ -0,0 +1,173 @@ +/****************************************************************************** + * Cal - A command line calendar utility + * + * Copyright (c) 2019-2022 Michael Fross + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ******************************************************************************/ +package org.fross.cal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import com.beust.jcommander.JCommander; + +/** + * @author Michael Fross (michael@fross.org) + * + */ +class CommandLineArgsTest { + + /** + * Test short command line arguments + */ + @Test + void testShortCommandLineArgs() { + String[] argv1 = { "-D", "-z", "-h", "-v", "-n", "2" }; + + CommandLineArgs cli = new CommandLineArgs(); + JCommander jc = new JCommander(); + jc.setProgramName("cal"); + + jc = JCommander.newBuilder().addObject(cli).build(); + jc.parse(argv1); + + assertTrue(cli.clDebug); + assertTrue(cli.clNoColor); + assertTrue(cli.clVersion); + assertTrue(cli.clHelp); + assertEquals(2, cli.clNum); + } + + /** + * Test long command line arguments + */ + @Test + void testLongCommandLineArgs() { + String[] argv2 = { "--debug", "--no-color", "--help", "--version", "--num", "6" }; + + CommandLineArgs cli = new CommandLineArgs(); + JCommander jc = new JCommander(); + jc.setProgramName("cal"); + + jc = JCommander.newBuilder().addObject(cli).build(); + jc.parse(argv2); + + assertTrue(cli.clDebug); + assertTrue(cli.clNoColor); + assertTrue(cli.clVersion); + assertTrue(cli.clHelp); + assertEquals(6, cli.clNum); + } + + /** + * Test mixed long and short options + */ + @Test + void testMixedCommandLineArgs() { + // Test Mix of Options + String[] argv3 = { "--no-color", "-?", "--num", "12" }; + + CommandLineArgs cli = new CommandLineArgs(); + JCommander jc = new JCommander(); + jc.setProgramName("cal"); + + jc = JCommander.newBuilder().addObject(cli).build(); + jc.parse(argv3); + + assertFalse(cli.clDebug); + assertTrue(cli.clNoColor); + assertFalse(cli.clVersion); + assertTrue(cli.clHelp); + assertEquals(12, cli.clNum); + } + + /** + * Test Month / Year parameter combinations - No Arguments + */ + @Test + void testParametersNoArgs() { + // Determine the current month and year at the time of execution of the test + int currentMonth = CommandLineArgs.queryMonthToUse(); + int currentYear = CommandLineArgs.queryYearToUse(); + + // Testing no Arguments. Result: Month=CurrentMonth | Year: CurrentYear + String[] args0 = {}; + CommandLineArgs.ProcessCommandLine(args0); + assertEquals(currentMonth, CommandLineArgs.queryMonthToUse()); + assertEquals(currentYear, CommandLineArgs.queryYearToUse()); + } + + /** + * Test Month / Year parameter combinations - One Arguments + */ + @Test + void testParametersOneArg() { + // Determine the current month and year at the time of execution of the test + int currentMonth = CommandLineArgs.queryMonthToUse(); + int currentYear = CommandLineArgs.queryYearToUse(); + + // Desired Result: Month=1 | Year: CurrentYear + String[] args1 = { "1" }; + CommandLineArgs.ProcessCommandLine(args1); + assertEquals(1, CommandLineArgs.queryMonthToUse()); + assertEquals(currentYear, CommandLineArgs.queryYearToUse()); + + // Desired Result: Month=12 | Year: CurrentYear + String[] args12 = { "12" }; + CommandLineArgs.ProcessCommandLine(args12); + assertEquals(12, CommandLineArgs.queryMonthToUse()); + assertEquals(currentYear, CommandLineArgs.queryYearToUse()); + + // Desired Result: Month=CurrentMonth | Year: 13 + String[] args13 = { "13" }; + CommandLineArgs.ProcessCommandLine(args13); + assertEquals(currentMonth, CommandLineArgs.queryMonthToUse()); + assertEquals(13, CommandLineArgs.queryYearToUse()); + + // Desired Result: Month=CurrentMonth | Year: 2300 + String[] args2300 = { "2300" }; + CommandLineArgs.ProcessCommandLine(args2300); + assertEquals(currentMonth, CommandLineArgs.queryMonthToUse()); + assertEquals(2300, CommandLineArgs.queryYearToUse()); + } + + /** + * Test Month / Year parameter combinations - Two Arguments + */ + @Test + void testParametersTwoArgs() { + // Desired Result: Month=5 | Year: 2023 + String[] args1 = { "5", "2023" }; + CommandLineArgs.ProcessCommandLine(args1); + assertEquals(5, CommandLineArgs.queryMonthToUse()); + assertEquals(2023, CommandLineArgs.queryYearToUse()); + + // Desired Result: Month=12 | Year: 3000 + String[] args2 = { "12", "3000" }; + CommandLineArgs.ProcessCommandLine(args2); + assertEquals(12, CommandLineArgs.queryMonthToUse()); + assertEquals(3000, CommandLineArgs.queryYearToUse()); + } + +}