1 /**
2 *
3 */
4 package net.sf.flatpack.examples;
5
6 import java.io.BufferedReader;
7 import java.io.IOException;
8 import java.io.InputStreamReader;
9 import java.io.PrintStream;
10 import java.lang.reflect.Method;
11 import java.math.BigDecimal;
12 import java.text.ParseException;
13 import java.text.SimpleDateFormat;
14 import java.util.ArrayList;
15 import java.util.Date;
16 import java.util.List;
17
18 /**
19 * @author Benoit Xhenseval
20 *
21 */
22 public class ConsoleMenu {
23 private static final int EXIT_CODE = 0;
24
25 private static final int SCREEN_COLUMNS = 110;
26
27 private static final int COL = 10;
28
29 private static final PrintStream OUT = System.out;
30
31 private final List menu = new ArrayList();
32
33 private final Repeater target;
34
35 private int screenColumns = SCREEN_COLUMNS;
36
37 private final List methods = new ArrayList();
38
39 private final List askForRepeat = new ArrayList();
40
41 /**
42 * @param containingObject
43 * the object that will be called back once an option is chosen
44 * in the menu
45 */
46 public ConsoleMenu(final Repeater containingObject) {
47 target = containingObject;
48 }
49
50
51
52
53 /**
54 * add an entry in the menu, sequentially
55 *
56 * @param menuDisplay
57 * how the entry will be displayed in the menu
58 * @param methodName
59 * name of the public method
60 * @param askForRepeat call back for repeat
61 */
62 public void addMenuItem(final String menuDisplay, final String methodName, final boolean repeat) {
63 menu.add(menuDisplay);
64 methods.add(methodName);
65 askForRepeat.add(Boolean.valueOf(repeat));
66 }
67
68 public void setScreenColumns(final int width) {
69 screenColumns = width;
70 }
71
72 /**
73 * display the menu, the application goes into a loop which provides the
74 * menu and fires the entries selected. It automatically adds an entry to
75 * exit.
76 */
77 public void displayMenu() {
78 while (true) {
79 ConsoleMenu.println("");
80 ConsoleMenu.println("Menu Options");
81
82 final int size = menu.size();
83
84 displayMenu(size);
85
86 int opt = -1;
87
88 do {
89 opt = ConsoleMenu.getInt("Enter your choice:", -1);
90 } while (((opt <= 0) || (opt > methods.size())) && (opt != EXIT_CODE));
91
92 if (opt == EXIT_CODE) {
93 ConsoleMenu.println("Exiting menu");
94 try {
95 final Method meth = target.getClass().getMethod("tearDown", new Class[0]);
96 if (meth != null) {
97 meth.invoke(target, new Object[0]);
98 }
99 } catch (final Exception e) {
100 e.printStackTrace();
101 }
102
103 return;
104 }
105
106
107 final String method = (String) methods.get(opt - 1);
108 final Boolean repeat = (Boolean) askForRepeat.get(opt - 1);
109
110 try {
111 final Method meth = target.getClass().getMethod(method, new Class[0]);
112 if (repeat.booleanValue()) {
113 target.repeat(meth);
114 } else {
115 meth.invoke(target, new Object[0]);
116 }
117 } catch (final Exception e) {
118 e.printStackTrace();
119 }
120 }
121 }
122
123 private void displayMenu(final int size) {
124 for (int i = 0; i < (size / 2); i++) {
125 final StringBuffer line = new StringBuffer();
126 final String col1 = (String) menu.get(i);
127
128 if ((i + 1) < COL) {
129 line.append(" ");
130 }
131
132 final int pos = i + 1;
133 line.append(" ").append(pos).append(") ").append(col1);
134
135 while (line.length() < (screenColumns / 2)) {
136 line.append(" ");
137 }
138
139 if ((i + (size / 2)) < size) {
140 final String col2 = (String) menu.get(i + (size / 2));
141 final int position = i + 1 + (size / 2);
142 line.append(" ").append(position).append(") ").append(col2);
143 }
144
145 ConsoleMenu.println(line.toString());
146 }
147
148 if (size % 2 != 0) {
149 final StringBuffer line = new StringBuffer();
150 final String col1 = (String) menu.get(size - 1);
151
152 if (size < COL) {
153 line.append(" ");
154 }
155
156 line.append(" ").append(size).append(") ").append(col1);
157
158 while (line.length() < (screenColumns / 2)) {
159 line.append(" ");
160 }
161
162 line.append(" ").append(EXIT_CODE).append(") ").append("Exit");
163 ConsoleMenu.println(line.toString());
164 } else {
165 ConsoleMenu.println(" " + EXIT_CODE + ") Exit");
166 }
167 }
168
169 /**
170 * Gets an int from the System.in
171 *
172 * @param title
173 * for the command line
174 * @return int as entered by the user of the console app
175 */
176 public static int getInt(final String title, final int defaultValue) {
177 int opt = -1;
178
179 do {
180 try {
181 final String val = ConsoleMenu.getString(title + " (default:" + defaultValue + ")", null);
182 if (val.length() == 0) {
183 opt = defaultValue;
184 } else {
185 opt = Integer.parseInt(val);
186 }
187 } catch (final NumberFormatException e) {
188 opt = -1;
189 }
190 } while (opt == -1);
191
192 return opt;
193 }
194
195 /**
196 * Gets a boolean from the System.in
197 *
198 * @param title
199 * for the command line
200 * @return boolean as selected by the user of the console app
201 */
202 public static boolean getBoolean(final String title, final boolean defaultValue) {
203 final String val =
204 ConsoleMenu.selectOne(title, new String[] { "Yes", "No" }, new String[] { Boolean.TRUE.toString(), Boolean.FALSE.toString() },
205 defaultValue ? 1 : 2);
206
207 return Boolean.valueOf(val).booleanValue();
208 }
209
210 /**
211 * Gets an BigDecimal from the System.in
212 *
213 * @param title
214 * for the command line
215 * @return int as entered by the user of the console app
216 */
217 public static BigDecimal getBigDecimal(final String title, final BigDecimal defaultValue) {
218 BigDecimal opt = null;
219
220 do {
221 try {
222 final String val = ConsoleMenu.getString(title + " (default:" + defaultValue + ")", null);
223 if (val.length() == 0) {
224 opt = defaultValue;
225 } else {
226 opt = new BigDecimal(val);
227 }
228 } catch (final NumberFormatException e) {
229 opt = null;
230 }
231 } while (opt == null && defaultValue != null);
232
233 return opt;
234 }
235
236 public static Date getDate(final String title, final Date defaultValue) {
237 final SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yy");
238 final String date =
239 ConsoleMenu.getString(title + "(dd-MM-yy" + (defaultValue != null ? ", default:" + fmt.format(defaultValue) : "") + ")", null);
240 try {
241 if (date == null || date.length() == 0) {
242 return defaultValue;
243 }
244 return fmt.parse(date);
245 } catch (final ParseException e) {
246
247 e.printStackTrace();
248 }
249 return null;
250 }
251
252 /**
253 * Gets a String from the System.in
254 *
255 * @param msg
256 * for the command line
257 * @return String as entered by the user of the console app
258 */
259 public static String getString(final String msg, final String defaultValue) {
260 ConsoleMenu.print(msg + (defaultValue != null ? " (default:" + defaultValue + ") " : ""));
261
262 BufferedReader bufReader = null;
263 String opt = null;
264
265 try {
266 bufReader = new BufferedReader(new InputStreamReader(System.in));
267 opt = bufReader.readLine();
268 } catch (final IOException ex) {
269 ex.printStackTrace();
270 System.exit(1);
271 }
272
273 if ((opt == null || opt.length() == 0) && defaultValue != null && defaultValue.length() > 0) {
274 opt = defaultValue;
275 }
276
277 return opt;
278 }
279
280 /**
281 * Generates a menu with a list of options and return the value selected.
282 *
283 * @param title
284 * for the command line
285 * @param optionNames
286 * name for each option
287 * @param optionValues
288 * value for each option
289 * @return String as selected by the user of the console app
290 */
291 public static String selectOne(final String title, final String[] optionNames, final String[] optionValues, final int defaultOption) {
292 if (optionNames.length != optionValues.length) {
293 throw new IllegalArgumentException("option names and values must have same length");
294 }
295
296 ConsoleMenu.println("Please chose " + title + " (default:" + defaultOption + ")");
297
298 for (int i = 0; i < optionNames.length; i++) {
299 ConsoleMenu.println(i + 1 + ") " + optionNames[i]);
300 }
301
302 int choice = 0;
303
304 do {
305 choice = ConsoleMenu.getInt("Your Choice 1-" + optionNames.length + ": ", defaultOption);
306 } while ((choice <= 0) || (choice > optionNames.length));
307
308 return optionValues[choice - 1];
309 }
310
311 /**
312 * @param prompt
313 * The prompt to display to the user.
314 * @return The password as entered by the user.
315 */
316 public static String getPassword(final String prompt) {
317 try {
318
319 final StringBuffer password = new StringBuffer();
320 final PasswordHidingThread maskingthread = new PasswordHidingThread(prompt);
321 final Thread thread = new Thread(maskingthread);
322 thread.start();
323
324
325 while (true) {
326 char c = (char) System.in.read();
327
328
329 maskingthread.stopMasking();
330
331 if (c == '\r') {
332 c = (char) System.in.read();
333
334 if (c == '\n') {
335 break;
336 } else {
337 continue;
338 }
339 } else if (c == '\n') {
340 break;
341 } else {
342
343 password.append(c);
344 }
345 }
346
347 return password.toString();
348 } catch (final IOException e) {
349 e.printStackTrace();
350 }
351
352 return null;
353 }
354
355
356
357
358 /**
359 * This class attempts to erase characters echoed to the console.
360 */
361 static class PasswordHidingThread extends Thread {
362 private boolean stop = false;
363
364 private final String prompt;
365
366 /**
367 * @param prompt
368 * The prompt displayed to the user
369 */
370 public PasswordHidingThread(final String prompt) {
371 this.prompt = prompt;
372 }
373
374 /**
375 * Begin masking until asked to stop.
376 */
377 public void run() {
378 while (!stop) {
379 try {
380
381 Thread.sleep(1);
382 } catch (final InterruptedException iex) {
383 iex.printStackTrace();
384 }
385
386 if (!stop) {
387 ConsoleMenu.print("\r" + prompt + " \r" + prompt);
388 }
389
390 System.out.flush();
391 }
392 }
393
394 /**
395 * Instruct the thread to stop masking.
396 */
397 public void stopMasking() {
398 this.stop = true;
399 }
400 }
401
402 private static void println(final String txt) {
403 OUT.println(txt);
404 }
405
406 private static void print(final String txt) {
407 OUT.print(txt);
408 }
409 }