As a follow on from ‘A little bit more Java’, this time we will progress to look at how we can input from the command line and enhance our program even further. The beginnings of a program can be an uphill struggle as we work away to get something that actually does something. Now we’ve made progress, that hill will start to soften and we’ll be able to add more functionality now that we have our base.
Disclaimers
Ubuntu® is a registered trademark of Canonical Ltd – ubuntu.com/legal/intellectual-property-policy . Other names / logos can be trademarks of their respective owners. Please review their website for details. I am independent from the organisations mentioned and am in no way writing for or endorsed by them. The information presented in this article is written according to my own understanding, there could be technical inaccuracies, so please do undertake your own research.
References
- ArrayList - docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ArrayList.html .
- Iterator - docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Iterator.html .
The parameters
Command line parameters are another way of getting input into a program. They can be especially useful when they provide configuration options specific to your needs, and / or you need to schedule the run for another time with specific data. The parameters can be anything that is suitable to be inputted in this way, i.e. relatively small elements of data. With our program we not only have configuration parameters, but also the text to flip too.
If we run the program with the help parameter, -?, then the program will tell us all of the options:

The implementation
As before, I have commented the code throughout to explain what each part does:
1/*
2 * Flipper
3 *
4 * Flips an entered string with a simple substitution cypher.
5 *
6 * @copyright 2022 G J Barnard
7 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8 */
9
10// Import classes we need.
11import java.io.BufferedReader;
12import java.io.InputStreamReader;
13import java.util.ArrayList;
14import java.util.HashMap;
15import java.util.Iterator;
16
17/**
18 * Flipper class.
19 * @author G J Barnard
20 */
21public class Flipper {
22 private InputStreamReader isr = null; // Stream to get the key presses from the keyboard.
23 private BufferedReader br = null; // Buffer to store the pressed keys from the input stream.
24 private HashMap<Character, Character> map = null; // Map to store the character to character relationship.
25 private ArrayList<String> ourArgs = null; // Arguments.
26
27 private boolean DEBUG = false; // Show the debug messages. Static so recompile when change.
28 private int DIFFERENCE = 4; // How many characters to 'shift to the left' the characters we flip.
29
30 // Characters we flip, both strings must be the same length so that the combined tally of the two is even.
31 private static final String LOWER = "abcdefghijklmnopqrstuvwxyz@*.:";
32 private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ&%#;";
33
34 /**
35 * Main program entry point.
36 * @param args Not used.
37 */
38 public static void main(String args[]) {
39 Flipper us = new Flipper(args); // Instantiate our class.
40
41 System.out.println("Flipped: " + us.flip("Flip : ")); // Flip with the before and after string prefixes.
42 }
43
44 /**
45 * Constructor to setup the input and create the character code map.
46 * @param args The arguments.
47 */
48 public Flipper(String args[]) {
49 // Check arguments.
50 this.checkArgs(args);
51
52 // Convert the string to characters we can access via an array index with a number.
53 char[] lowerChars = LOWER.toCharArray();
54 char[] upperChars = UPPER.toCharArray();
55 // Array indexes start at zero and not one, but the length is the actual number of characters.
56 int lowerMax = lowerChars.length - 1;
57 // Ditto for indexing with the difference.
58 int difference = this.DIFFERENCE - 1;
59 int fromIndex; // From character.
60 int toIndex; // To character.
61
62 // Confirm our logic for looping around the toIndex when its larger than the number of characters.
63 if (this.DEBUG) {
64 System.out.println("Difference is: " + this.DIFFERENCE);
65 System.out.println();
66 System.out.println("Loop check:");
67 for (int index = 0; index <= lowerMax; index++) {
68 fromIndex = index + difference;
69 if (fromIndex > lowerMax) {
70 fromIndex = fromIndex - lowerMax;
71 }
72 System.out.println(index + " -> " + fromIndex);
73 }
74 System.out.println();
75 }
76
77 int half = (lowerMax + 1) / 2; // Find the mid point in the character string.
78 int mapLength = lowerChars.length * 2; // Work out how big the map will be.
79 this.map = new HashMap<>(mapLength); // Construct the map, character to character.
80 if (this.DEBUG) {
81 System.out.println("Mappings:");
82 }
83 for (int index = 0; index <= half; index++) {
84 fromIndex = index + difference; // Shift.
85 if (fromIndex > lowerMax) {
86 fromIndex = fromIndex - lowerMax;
87 }
88
89 toIndex = (lowerMax - index) + difference; // Flip.
90 if (toIndex > lowerMax) {
91 toIndex = (toIndex - 1) - lowerMax;
92 }
93
94 // Check the mappings.
95 if (this.DEBUG) {
96 System.out.println(
97 index + " (From: " + fromIndex + " To: " + toIndex + ")" +
98 ": " + lowerChars[fromIndex] + " -> " + lowerChars[toIndex] + " -> " + lowerChars[fromIndex] +
99 " & " + upperChars[fromIndex] + " -> " + upperChars[toIndex] + " -> " + upperChars[fromIndex]
100 );
101 }
102
103 // Add the key value pairs to the map. For example, a -> z and back again, z -> a.
104 this.map.put(lowerChars[fromIndex], lowerChars[toIndex]);
105 this.map.put(lowerChars[toIndex], lowerChars[fromIndex]);
106 this.map.put(upperChars[fromIndex], upperChars[toIndex]);
107 this.map.put(upperChars[toIndex], upperChars[fromIndex]);
108 }
109
110 // Check that the map is what we intended.
111 if (this.DEBUG) {
112 System.out.println();
113 System.out.println("The map is:");
114 for (int index = 0; index < lowerChars.length; index++) {
115 System.out.println(
116 index + ": " + lowerChars[index] + " -> " + this.map.get(lowerChars[index]) +
117 " & " + upperChars[index] + " -> " + this.map.get(upperChars[index])
118 );
119 }
120 System.out.println();
121 }
122 }
123
124 /**
125 * Check the arguments
126 *
127 * Note: The exit status code, derived from 'C's 'errno.h', see: https://en.wikipedia.org/wiki/Errno.h
128 *
129 * @param args The arguments.
130 */
131 private void checkArgs(String args[]) {
132 if (args.length > 0) {
133 for (String var: args) {
134 if (var.charAt(0) == '-') { // Argument prefix.
135 String param = var.substring(1);
136 if (param.charAt(0) == '?') { // Help.
137 this.displayHelp();
138 System.exit(0); // Bye!
139 }
140 if (param.charAt(0) == 'd') { // Debug.
141 // Debug.
142 this.DEBUG = true;
143 continue;
144 }
145 try { // If a number then this is the difference to use instead of the default assigned during construction.
146 this.DIFFERENCE = Integer.parseInt(param);
147 } catch (NumberFormatException nfe) { // Ops!
148 System.out.println("Invalid argument: " + param);
149 System.out.println();
150 this.displayHelp();
151 System.exit(22); // EINVAL - Invalid argument.
152 }
153 } else { // We have text to flip. The command line is delimited by a space, so we can have many words.
154 if (this.ourArgs == null) {
155 this.ourArgs = new ArrayList<>();
156 }
157 this.ourArgs.add(var);
158 }
159 }
160 }
161 }
162
163 /**
164 * Display the help.
165 */
166 private void displayHelp() {
167 System.out.println("Usage: ");
168 System.out.println("java Flipper [arguments] [text to flip or leave empty to be asked]");
169 System.out.println("Optional arguments:");
170 System.out.println("-[number] The difference.");
171 System.out.println("-d Turn on debugging.");
172 System.out.println("-? Show This help.");
173 }
174
175 /**
176 * Flip the entered text.
177 * @param message What to show as the input question.
178 * @return
179 */
180 private String flip(String message) {
181 String input;
182
183 if (this.ourArgs == null) { // No text to flip, so ask.
184 System.out.print(message);
185
186 this.isr = new InputStreamReader(System.in); // Attach a stream to the system input.
187 this.br = new BufferedReader(this.isr); // Attach a buffer to store the pressed keys.
188
189 // Using a 'try / catch' block as reading key presses is an input output operation that can fail.
190 try {
191 input = br.readLine(); // Read a line of text from the user.
192 } catch (java.io.IOException ioe) { // An error has happened when getting the text from the user.
193 // Tell the user what the error is.
194 System.out.println(); // New line after the message.
195 this.processError(ioe);
196 return ("");
197 }
198 } else { // Get the text supplied on the command line and concatenate into one line.
199 input = new String();
200 Iterator<String> args = this.ourArgs.iterator(); // Iterate over each word.
201 while (args.hasNext()) { // Another word?
202 input += args.next(); // Get the word.
203 if (args.hasNext()) {
204 input += " "; // Put the spaces back.
205 }
206 }
207 }
208
209 /* Using a 'try / catch' block as pressing 'Ctrl-C' during input caused the 'input' to be null and
210 so the 'toCharArray()' call fails. */
211 char[] inputChars;
212 try {
213 // Convert the text from the user (string) into an array of indexable characters.
214 inputChars = input.toCharArray();
215 } catch (java.lang.NullPointerException npe) {
216 // Tell the user what the error is.
217 System.out.println();
218 this.processError(npe);
219 return ("");
220 }
221
222 Character theChar; // Temporary store for the character in the 'type' we need it in from the map.
223
224 // Process each character in the input text from the user.
225 for (int index = 0; index < inputChars.length; index++) {
226 theChar = this.map.get(inputChars[index]); // Get the mapped character, key -> value.
227 if (theChar != null) { // If the character is mapped then flip, otherwise it will not be transposed.
228 /* Replace the entered character with the mapped one. Being clear here with the Character to char
229 type conversion. Removing 'charValue()' call will still work. */
230 inputChars[index] = theChar.charValue();
231 }
232 }
233
234 return new String(inputChars); // Return the flipped entered text as a string we can output.
235 }
236
237 /**
238 * Tell the user what happened.
239 * @param ex The exception to process.
240 */
241 private void processError(java.lang.Exception ex) {
242 System.err.print("Ops! -> "); // For some reason string concatenation does not work here.
243 System.err.print(ex.getLocalizedMessage()); // What happened.
244 System.err.print(" @ "); // And.
245 System.err.println(ex.getStackTrace()[0]); // Where.
246 }
247}
What’s changed?
In essence:
- A new method called ‘
checkArgs’ to check and interpret any arguments. - A new method called ‘
displayHelp’, to well, display the help. - The method ‘
flip’ has been changed to detect if there is text from the arguments to flip, and if so uses it instead of asking. - The
DEBUGandDIFFERENCEattributes can now be set dynamically. - I’ve added introductions to the debug information.
If you’d like to compare the changes then please look at: github.com/gjb2048/code/compare/0ff7399~3...0ff7399 .
Running
And so with the arguments of ‘-24 This is eLearningWorld’ we get:

Then if we flip back, then we get:

But! We have an issue with the text, so we need to encapsulate the text in some quotes:

Now it works!
If you’d like to try for yourself and don’t want to copy / paste from here, then you can get a copy from: raw.githubusercontent.com/gjb2048/code/0ff7399/Java/Flipper.java .
Conclusion
Do have a go.