From 9ed1218062e61bd161f97eb9abde723d50044876 Mon Sep 17 00:00:00 2001 From: Michael Fross Date: Thu, 15 Feb 2024 10:02:15 -0600 Subject: [PATCH] Code Optimization Improved command parsing Updates to userguide export command. No backslashes! LR calc optimization Additional exception processing Grammer/spelling Much is based on IntelliJ recommendations --- mdbook/src/Chapters/OperationalCommands.md | 2 +- pom.xml | 2 +- snap/snapcraft.yaml | 2 +- .../java/org/fross/rpncalc/CommandParser.java | 8 ++++--- src/main/java/org/fross/rpncalc/Main.java | 23 +++++++++++------- .../java/org/fross/rpncalc/StackCommands.java | 24 +++++++++---------- .../java/org/fross/rpncalc/StackMemory.java | 8 +++---- src/main/java/org/fross/rpncalc/StackObj.java | 3 --- .../org/fross/rpncalc/StackOperations.java | 20 +++++++++------- .../java/org/fross/rpncalc/UserFunctions.java | 17 +++++++------ .../fross/rpncalc/StackOperationsTest.java | 17 +++++++++---- 11 files changed, 69 insertions(+), 57 deletions(-) diff --git a/mdbook/src/Chapters/OperationalCommands.md b/mdbook/src/Chapters/OperationalCommands.md index 4b3e6d0..72e64f7 100644 --- a/mdbook/src/Chapters/OperationalCommands.md +++ b/mdbook/src/Chapters/OperationalCommands.md @@ -8,7 +8,7 @@ Operational commands are commands that do not directly impact your stack numbers |-------|-------------| |debug|Toggle debug mode which will display additional information on what's happening internally in the program. Same as the `-D` command line switch. Probably not the useful for a normal user| |h
?|`h` or `?` will display the in-program help page| -|export `FILE`|`Export` will simply export the current stack values into the file specified. The format is very simple with one number per line. The output will be ordered as on the screen with the top of the stack item at the end of the file and the last stack item at the top. If the file exists, it will be overwritten.| +|export `FILE`|`Export` will simply export the current stack values into the file specified. The format is very simple with one number per line. The output will be ordered as on the screen with the top of the stack item at the end of the file and the last stack item at the top. If the file exists, it will be overwritten.

NOTE: Please ensure the backslash (`/`) is used as a directory separator, even on Windows. Backslashes (`\`) are **NOT** suported and are removed when the command is entered| |import `FILE`|With `import` RPNCalc will replace the current stack with one loaded from a file. The file format is simple, just one number per line. Do not include any comments or alphanumeric/special characters. Just one number per line with the last number being `line1` - just like the display in RPNCalc| |list stacks|List the current saved stacks on the system, including the one currently in use| |list mem|Display the contents of the memory slots| diff --git a/pom.xml b/pom.xml index 635cfc6..9ccb2ec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.fross rpncalc - 5.2.2 + 5.2.3 jar rpncalc diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 3f23f5f..905e317 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: rpncalc -version: '5.2.2' +version: '5.2.3' summary: The command line Reverse Polish Notation (RPN) calculator description: | RPNCalc is an easy to use command line based Reverse Polish diff --git a/src/main/java/org/fross/rpncalc/CommandParser.java b/src/main/java/org/fross/rpncalc/CommandParser.java index c7fc1a7..f910c62 100644 --- a/src/main/java/org/fross/rpncalc/CommandParser.java +++ b/src/main/java/org/fross/rpncalc/CommandParser.java @@ -493,7 +493,7 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu calcStack.push(new BigDecimal(cmdInputCmd)); // Handle NumOps - numbers with a single operand at the end (*, /, +, -, ^) - } else if (cmdInputCmd.matches("^-?\\d*\\.?\\d*[Ee]?\\d*[\\*\\+\\-\\/\\^]")) { + } else if (cmdInputCmd.matches("^-?\\d*\\.?\\d*[Ee]?\\d*[*+\\-/^]")) { // Save current calcStack to the undoStack calcStack.saveUndo(); @@ -507,7 +507,7 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu Output.debugPrintln("NumOp Found: Num= '" + tempNum + "'"); Output.debugPrintln("NumOp Found: Op = '" + tempOp + "'"); calcStack.push(new BigDecimal(tempNum)); - calcStack = Math.Parse(tempOp, calcStack); + Math.Parse(tempOp, calcStack); } catch (NumberFormatException ex) { // Prevents a crash if user enters "-+" (which they shouldn't do) @@ -557,7 +557,9 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu } break; - } + + } // End of giant switch statement + } } \ No newline at end of file diff --git a/src/main/java/org/fross/rpncalc/Main.java b/src/main/java/org/fross/rpncalc/Main.java index 5b2a335..ff4b3ba 100644 --- a/src/main/java/org/fross/rpncalc/Main.java +++ b/src/main/java/org/fross/rpncalc/Main.java @@ -110,8 +110,8 @@ public static void DisplayStatusLine() { * @param args */ public static void main(String[] args) { - String cmdInput = ""; // What the user enters in totality - String cmdInputCmd = ""; // The first field - the command + String cmdInput = ""; // What the user enters in totality + String cmdInputCmd = ""; // The first field - the command String cmdInputParam = ""; // The remaining string - Parameters Preferences prefConfig = Preferences.userRoot().node("/org/fross/rpn/config"); // Persistent configuration settings @@ -242,6 +242,7 @@ public static void main(String[] args) { // Input command from user try { cmdInput = scanner.readLine("\n" + INPUT_PROMPT); + } catch (UserInterruptException ex) { // User entered Ctrl-C so exit the program gracefully by placing the "exit" command as the input cmdInput = "exit"; @@ -252,6 +253,12 @@ public static void main(String[] args) { Output.fatalError("Could not read user input\n" + ex.getMessage(), 5); } + // If nothing was entered, stop processing and request new input + if (cmdInput.isEmpty()) { + Output.debugPrintln("Blank line entered"); + continue; + } + // Break each entered line (cmdInput) into a command (cmdInputCmd) and a parameter (cmdInputParam) string try { // Remove any commas from the string allowing for numbers such as "12,123" to be entered @@ -264,11 +271,9 @@ public static void main(String[] args) { cmdInputParam = ci[1]; } catch (ArrayIndexOutOfBoundsException e) { - // Ignore this exception if there is no command or parameter entered - if (cmdInputCmd.isEmpty()) { - Output.debugPrintln("Blank line entered"); - continue; - } + // TODO: Ignore this error as it will trigger if just a command is entered with no parameter. There must be a better way... + } catch (Exception e) { + Output.printColorln(Ansi.Color.RED, "ERROR: Problem parsing the command: '" + cmdInput + "' into command and arguments"); } // While in debug mode, show the entered text along with the broken up command and parameter @@ -282,11 +287,11 @@ public static void main(String[] args) { // Call the parser to send the command to the correct function to execute CommandParser.Parse(calcStack, calcStack2, cmdInput, cmdInputCmd, cmdInputParam); - // Clear input parameters before we start again + // Clear input parameters before we start again with the next command cmdInputCmd = ""; cmdInputParam = ""; - } // End While Loop + } // End While Command Loop // If recording is on, complete the recording off process before exiting if (UserFunctions.recordingIsEnabled()) { diff --git a/src/main/java/org/fross/rpncalc/StackCommands.java b/src/main/java/org/fross/rpncalc/StackCommands.java index 0401202..2db6102 100644 --- a/src/main/java/org/fross/rpncalc/StackCommands.java +++ b/src/main/java/org/fross/rpncalc/StackCommands.java @@ -60,7 +60,7 @@ public static void cmdAddAll(StackObj calcStack, String arg) { keepFlag = true; } } catch (StringIndexOutOfBoundsException ex) { - keepFlag = false; + // keepFlag is already set to false, so just ignore this error } // Counter to hold the accumulating total @@ -128,7 +128,7 @@ public static void cmdAverage(StackObj calcStack, String arg) { keepFlag = true; } } catch (StringIndexOutOfBoundsException ex) { - keepFlag = false; + // keepFlag is already false so just ignore this error } // Calculate the mean @@ -233,13 +233,13 @@ public static void cmdDelete(StackObj calcStack, String arg) { // Determine line to delete by looking at arg try { - // If this doesn't have an exception, user entered in a single number integer + // If this doesn't have an exception, user has entered a single number integer // Simple delete that line startLine = Integer.parseInt(arg); endLine = startLine; } catch (NumberFormatException ex) { - // If a dash is present, user entered in a range + // If a dash is present, user entered a range if (arg.contains("-")) { try { startLine = Integer.parseInt(arg.split("-")[0]); @@ -439,11 +439,9 @@ public static void cmdLinearRegression(StackObj calcStack, String args) { if (a.toLowerCase().startsWith("a")) { argAdd = true; Output.debugPrintln("Setting LR ADD flag"); - continue; } else { argX = new BigDecimal(a).subtract(BigDecimal.ONE); Output.debugPrintln("Setting LR 'x' value to : " + argX); - continue; } } } @@ -500,8 +498,8 @@ public static void cmdLinearRegression(StackObj calcStack, String args) { BigDecimal a = a_top.divide(a_bottom, MathContext.DECIMAL128); BigDecimal b_top = n.multiply(sumXY).subtract(sumX.multiply(sumY)); - BigDecimal b_bottom = n.multiply(sumX2).subtract(sumX.pow(2)); - BigDecimal b = b_top.divide(b_bottom, MathContext.DECIMAL128); + // b_bottom is the same as a_bottom so just it that + BigDecimal b = b_top.divide(a_bottom, MathContext.DECIMAL128); // Output details if debug is enabled Output.debugPrintln("n: " + n.toPlainString()); @@ -595,7 +593,7 @@ public static boolean cmdMaximum(StackObj calcStack) { largestValue = calcStack.get(i); } - // Add lowest value to the stack + // Add the lowest value to the stack calcStack.push(largestValue); return true; @@ -625,7 +623,7 @@ public static void cmdMedian(StackObj calcStack, String arg) { keepFlag = true; } } catch (StringIndexOutOfBoundsException ex) { - keepFlag = false; + // keepFlag is already false so just ignore this error } // Calculate the median @@ -664,7 +662,7 @@ public static boolean cmdMinimum(StackObj calcStack) { lowestValue = calcStack.get(i); } - // Add lowest value to the stack + // Add the lowest value to the stack calcStack.push(lowestValue); return true; @@ -728,7 +726,7 @@ public static void cmdOperand(StackObj calcStack, String op) { public static void cmdRandom(StackObj calcStack, String param) { long low = 1L; long high = 100L; - long randomNumber = 0L; + long randomNumber; // Parse out the low and high numbers try { @@ -933,7 +931,7 @@ public static void cmdStdDeviation(StackObj calcStack, String arg) { keepFlag = true; } } catch (StringIndexOutOfBoundsException ex) { - keepFlag = false; + // keepFlag is already false so just ignore this error } // Step1: Get the mean diff --git a/src/main/java/org/fross/rpncalc/StackMemory.java b/src/main/java/org/fross/rpncalc/StackMemory.java index 7fec211..a5fb083 100644 --- a/src/main/java/org/fross/rpncalc/StackMemory.java +++ b/src/main/java/org/fross/rpncalc/StackMemory.java @@ -76,8 +76,8 @@ public static boolean SetMaxMemorySlots(String slots) { public static int QueryInUseMemorySlots() { int inUseCounter = 0; - for (int i = 0; i < memorySlots.length; i++) { - if (memorySlots[i] != null) inUseCounter++; + for (BigDecimal memorySlot : memorySlots) { + if (memorySlot != null) inUseCounter++; } return inUseCounter; @@ -138,8 +138,8 @@ public static void cmdMem(StackObj calcStack, String arg) { String[] argParse; int memSlot; - // Parse the command string provided. If we can't create an integer from the first - // arg then no stack number was provided + // Parse the command string provided. If we can't create an integer from the first arg then no stack number was provided + // arg contains either a command or a slot number + command try { argParse = arg.split(" "); memSlot = Integer.parseInt(argParse[0]); diff --git a/src/main/java/org/fross/rpncalc/StackObj.java b/src/main/java/org/fross/rpncalc/StackObj.java index 00e02e7..08dca51 100644 --- a/src/main/java/org/fross/rpncalc/StackObj.java +++ b/src/main/java/org/fross/rpncalc/StackObj.java @@ -268,9 +268,6 @@ public void sort(String mode) { sortedStack.push(tmpValue); } - // Clear the calcStack and replace the values with the sorted stack - calcStack.clear(); - // Fill the calcStack back with the sorted values from sortedStack if (mode.equalsIgnoreCase("ascending")) { // Ascending diff --git a/src/main/java/org/fross/rpncalc/StackOperations.java b/src/main/java/org/fross/rpncalc/StackOperations.java index 5290990..1e9cbdb 100644 --- a/src/main/java/org/fross/rpncalc/StackOperations.java +++ b/src/main/java/org/fross/rpncalc/StackOperations.java @@ -213,10 +213,14 @@ public static void exportStackToDisk(StackObj calcStack, String arg) { File file = new File(fileName); if (file.exists()) { Output.debugPrintln("'" + fileName + "' exists - deleting"); - file.delete(); + + // Delete the file. If it returns false, then throw an error + if (!file.delete()) { + throw new Exception("File Delete Failed"); + } } } catch (Exception ex) { - Output.printColorln(Ansi.Color.RED, "'" + fileName + "' exists and was not able to be deleted"); + Output.printColorln(Ansi.Color.RED, "'" + fileName + "' exists but could not be deleted\n" + ex.getMessage()); return; } @@ -239,7 +243,7 @@ public static void exportStackToDisk(StackObj calcStack, String arg) { return; } - Output.printColorln(Ansi.Color.CYAN, "Export successful to '" + fileName + "'"); + Output.printColorln(Ansi.Color.CYAN, "Export successful: '" + new File(fileName).getAbsoluteFile() + "'"); } /** @@ -265,9 +269,9 @@ public static void importStackFromDisk(StackObj calcStack, String arg) { calcStack.clear(); // Convert the strings to BigDecimal values. Skip empty lines - for (int i = 0; i < linesRead.size(); i++) { - if (!linesRead.get(i).isEmpty()) { - calcStack.push(new BigDecimal(String.valueOf(linesRead.get(i)))); + for (String s : linesRead) { + if (!s.isEmpty()) { + calcStack.push(new BigDecimal(s)); } } @@ -295,9 +299,8 @@ public static void importStackFromDisk(StackObj calcStack, String arg) { * @param stk Primary Stack * @param item1 First item to swap * @param item2 Second item to swap - * @return StackObj */ - public static StackObj StackSwapItems(StackObj stk, int item1, int item2) { + public static void StackSwapItems(StackObj stk, int item1, int item2) { int stkSize = stk.size(); BigDecimal[] tempArray = new BigDecimal[stkSize]; BigDecimal value1; @@ -324,7 +327,6 @@ public static StackObj StackSwapItems(StackObj stk, int item1, int item2) { stk.push(tempArray[i]); } - return (stk); } } diff --git a/src/main/java/org/fross/rpncalc/UserFunctions.java b/src/main/java/org/fross/rpncalc/UserFunctions.java index 6b7586f..35faf98 100644 --- a/src/main/java/org/fross/rpncalc/UserFunctions.java +++ b/src/main/java/org/fross/rpncalc/UserFunctions.java @@ -180,12 +180,12 @@ public static boolean recordingIsEnabled() { */ public static void RecordCommand(String arg) { // Ignore the following commands from recording - String[] ignore = {"list", "debug", "ver", "version", "h", "help", "?", "record", "rec", "function", "func", "reset", "cx", "x", "exit", "quit"}; + String[] commandsToIgnore = {"list", "debug", "ver", "version", "h", "help", "?", "record", "rec", "function", "func", "reset", "cx", "x", "exit", "quit"}; // If the command starts with an ignored item, just return before adding it to the recording - for (int i = 0; i < ignore.length; i++) { - if (arg.startsWith(ignore[i])) { - Output.debugPrintln("Record ignoring the command '" + ignore[i] + "'"); + for (String s : commandsToIgnore) { + if (arg.startsWith(s)) { + Output.debugPrintln("Record ignoring the command '" + s + "'"); return; } } @@ -286,12 +286,11 @@ public static void FunctionRun(StackObj calcStack, StackObj calcStack2, String f String[] ci = fullCommand.toLowerCase().trim().split("\\s+", 2); command = ci[0]; param = ci[1]; + } catch (ArrayIndexOutOfBoundsException e) { - // Ignore if there is no command or parameter entered - if (command.isEmpty()) { - Output.debugPrintln("Blank line entered"); - continue; - } + // TODO: Ignore this error as it will trigger if just a command is entered with no parameter. There must be a better way... + } catch (Exception e) { + Output.printColorln(Ansi.Color.RED, "ERROR: Problem parsing the command: '" + fullCommand + "' into command and arguments"); } Output.debugPrintln(" Step" + i + ": " + pChild.get("Step" + i, "Error")); diff --git a/src/test/java/org/fross/rpncalc/StackOperationsTest.java b/src/test/java/org/fross/rpncalc/StackOperationsTest.java index fd0516c..475321d 100644 --- a/src/test/java/org/fross/rpncalc/StackOperationsTest.java +++ b/src/test/java/org/fross/rpncalc/StackOperationsTest.java @@ -192,8 +192,10 @@ void testExport() { // Delete the testFile if it exists try { - testFile.delete(); - } catch (Exception ex) { + if (!testFile.delete()) { + throw new IOException("Delete Failed"); + } + } catch (IOException ex) { Output.println("Testing Export: Issue deleting test file: ' " + testFileName + "'"); } @@ -228,8 +230,12 @@ void testExport() { // Delete the test import file try { File file = new File(testFileName); - file.delete(); + + if (!file.delete()) { + throw new Exception("Unable to delete file"); + } assertFalse(testFile.exists()); + } catch (Exception ex) { Output.println("Testing Export: Issue deleting test file: ' " + testFileName + "'"); } @@ -272,8 +278,11 @@ void testImport() { // Delete the test import file try { File file = new File(testFileName); - file.delete(); + if (!file.delete()) { + throw new Exception("Unable to delete file"); + } assertFalse(file.exists()); + } catch (Exception ex) { Output.println("Testing Import: Issue deleting test file: ' " + testFileName + "'"); }