View Javadoc

1   /**
2    * 
3    */
4   package net.sf.pzfilereader.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      // ~ Methods
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             // now call the method
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 = ConsoleMenu.selectOne(title, new String[] { "Yes", "No" }, new String[] { Boolean.TRUE.toString(),
204                 Boolean.FALSE.toString() }, defaultValue ? 1 : 2);
205 
206         return Boolean.valueOf(val).booleanValue();
207     }
208 
209     /**
210      * Gets an BigDecimal from the System.in
211      * 
212      * @param title
213      *            for the command line
214      * @return int as entered by the user of the console app
215      */
216     public static BigDecimal getBigDecimal(final String title, final BigDecimal defaultValue) {
217         BigDecimal opt = null;
218 
219         do {
220             try {
221                 final String val = ConsoleMenu.getString(title + " (default:" + defaultValue + ")", null);
222                 if (val.length() == 0) {
223                     opt = defaultValue;
224                 } else {
225                     opt = new BigDecimal(val);
226                 }
227             } catch (final NumberFormatException e) {
228                 opt = null;
229             }
230         } while (opt == null && defaultValue != null);
231 
232         return opt;
233     }
234 
235     public static Date getDate(final String title, final Date defaultValue) {
236         final SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yy");
237         final String date = ConsoleMenu.getString(title + "(dd-MM-yy"
238                 + (defaultValue != null ? ", default:" + fmt.format(defaultValue) : "") + ")", null);
239         try {
240             if (date == null || date.length() == 0) {
241                 return defaultValue;
242             }
243             return fmt.parse(date);
244         } catch (final ParseException e) {
245             // TODO Auto-generated catch block
246             e.printStackTrace();
247         }
248         return null;
249     }
250 
251     /**
252      * Gets a String from the System.in
253      * 
254      * @param msg
255      *            for the command line
256      * @return String as entered by the user of the console app
257      */
258     public static String getString(final String msg, final String defaultValue) {
259         ConsoleMenu.print(msg + (defaultValue != null ? " (default:" + defaultValue + ") " : ""));
260 
261         BufferedReader bufReader = null;
262         String opt = null;
263 
264         try {
265             bufReader = new BufferedReader(new InputStreamReader(System.in));
266             opt = bufReader.readLine();
267         } catch (final IOException ex) {
268             ex.printStackTrace();
269             System.exit(1);
270         }
271 
272         if ((opt == null || opt.length() == 0) && defaultValue != null && defaultValue.length() > 0) {
273             opt = defaultValue;
274         }
275 
276         return opt;
277     }
278 
279     /**
280      * Generates a menu with a list of options and return the value selected.
281      * 
282      * @param title
283      *            for the command line
284      * @param optionNames
285      *            name for each option
286      * @param optionValues
287      *            value for each option
288      * @return String as selected by the user of the console app
289      */
290     public static String selectOne(final String title, final String[] optionNames, final String[] optionValues,
291             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             // password holder
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             // block until enter is pressed
325             while (true) {
326                 char c = (char) System.in.read();
327 
328                 // assume enter pressed, stop masking
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                     // store the password
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     // ~ Inner Classes
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                     // attempt masking at this rate
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 }