View Javadoc

1   /**
2    *	jline - Java console input library
3    *	Copyright (c) 2002, 2003, 2004, 2005, Marc Prud'hommeaux <mwp1@cornell.edu>
4    *	All rights reserved.
5    *
6    *	Redistribution and use in source and binary forms, with or
7    *	without modification, are permitted provided that the following
8    *	conditions are met:
9    *
10   *	Redistributions of source code must retain the above copyright
11   *	notice, this list of conditions and the following disclaimer.
12   *
13   *	Redistributions in binary form must reproduce the above copyright
14   *	notice, this list of conditions and the following disclaimer
15   *	in the documentation and/or other materials provided with
16   *	the distribution.
17   *
18   *	Neither the name of JLine nor the names of its contributors
19   *	may be used to endorse or promote products derived from this
20   *	software without specific prior written permission.
21   *
22   *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23   *	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24   *	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25   *	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26   *	EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
27   *	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
28   *	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29   *	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30   *	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31   *	AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32   *	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33   *	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34   *	OF THE POSSIBILITY OF SUCH DAMAGE.
35   */
36  package jline;
37  
38  import java.io.*;
39  import java.util.*;
40  
41  /**
42   *  A reader for console applications. It supports custom tab-completion,
43   *  saveable command history, and command line editing. On some
44   *  platforms, platform-specific commands will need to be
45   *  issued before the reader will function properly. See
46   *  {@link Terminal#initializeTerminal} for convenience methods for
47   *  issuing platform-specific setup commands.
48   *
49   *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
50   */
51  public class ConsoleReader
52  	implements ConsoleOperations
53  {
54  	String prompt;
55  
56  	public static final String CR = System.getProperty ("line.separator");
57  
58  
59  	/**
60  	 *  Map that contains the operation name to keymay operation mapping.
61  	 */
62  	public static SortedMap KEYMAP_NAMES;
63  
64  	static
65  	{
66  		Map names = new TreeMap ();
67  
68  		names.put ("MOVE_TO_BEG",			new Short (MOVE_TO_BEG));
69  		names.put ("MOVE_TO_END",			new Short (MOVE_TO_END));
70  		names.put ("PREV_CHAR",				new Short (PREV_CHAR));
71  		names.put ("NEWLINE",				new Short (NEWLINE));
72  		names.put ("KILL_LINE",				new Short (KILL_LINE));
73  		names.put ("PASTE",					new Short (PASTE));
74  		names.put ("CLEAR_SCREEN",			new Short (CLEAR_SCREEN));
75  		names.put ("NEXT_HISTORY",			new Short (NEXT_HISTORY));
76  		names.put ("PREV_HISTORY",			new Short (PREV_HISTORY));
77  		names.put ("REDISPLAY",				new Short (REDISPLAY));
78  		names.put ("KILL_LINE_PREV",		new Short (KILL_LINE_PREV));
79  		names.put ("DELETE_PREV_WORD",		new Short (DELETE_PREV_WORD));
80  		names.put ("NEXT_CHAR",				new Short (NEXT_CHAR));
81  		names.put ("REPEAT_PREV_CHAR",		new Short (REPEAT_PREV_CHAR));
82  		names.put ("SEARCH_PREV",			new Short (SEARCH_PREV));
83  		names.put ("REPEAT_NEXT_CHAR",		new Short (REPEAT_NEXT_CHAR));
84  		names.put ("SEARCH_NEXT",			new Short (SEARCH_NEXT));
85  		names.put ("PREV_SPACE_WORD",		new Short (PREV_SPACE_WORD));
86  		names.put ("TO_END_WORD",			new Short (TO_END_WORD));
87  		names.put ("REPEAT_SEARCH_PREV",	new Short (REPEAT_SEARCH_PREV));
88  		names.put ("PASTE_PREV",			new Short (PASTE_PREV));
89  		names.put ("REPLACE_MODE",			new Short (REPLACE_MODE));
90  		names.put ("SUBSTITUTE_LINE",		new Short (SUBSTITUTE_LINE));
91  		names.put ("TO_PREV_CHAR",			new Short (TO_PREV_CHAR));
92  		names.put ("NEXT_SPACE_WORD",		new Short (NEXT_SPACE_WORD));
93  		names.put ("DELETE_PREV_CHAR",		new Short (DELETE_PREV_CHAR));
94  		names.put ("ADD",					new Short (ADD));
95  		names.put ("PREV_WORD",				new Short (PREV_WORD));
96  		names.put ("CHANGE_META",			new Short (CHANGE_META));
97  		names.put ("DELETE_META",			new Short (DELETE_META));
98  		names.put ("END_WORD",				new Short (END_WORD));
99  		names.put ("NEXT_CHAR",				new Short (NEXT_CHAR));
100 		names.put ("INSERT",				new Short (INSERT));
101 		names.put ("REPEAT_SEARCH_NEXT",	new Short (REPEAT_SEARCH_NEXT));
102 		names.put ("PASTE_NEXT",			new Short (PASTE_NEXT));
103 		names.put ("REPLACE_CHAR",			new Short (REPLACE_CHAR));
104 		names.put ("SUBSTITUTE_CHAR",		new Short (SUBSTITUTE_CHAR));
105 		names.put ("TO_NEXT_CHAR",			new Short (TO_NEXT_CHAR));
106 		names.put ("UNDO",					new Short (UNDO));
107 		names.put ("NEXT_WORD",				new Short (NEXT_WORD));
108 		names.put ("DELETE_NEXT_CHAR",		new Short (DELETE_NEXT_CHAR));
109 		names.put ("CHANGE_CASE",			new Short (CHANGE_CASE));
110 		names.put ("COMPLETE",				new Short (COMPLETE));
111 		names.put ("EXIT",					new Short (EXIT));
112 		
113 		KEYMAP_NAMES = new TreeMap (Collections.unmodifiableMap (names));
114 	}
115 
116 
117 	/**
118 	 *  The map for logical operations.
119 	 */
120 	private final short[] keybindings;
121 
122 
123 	/**
124 	 *  If true, issue an audible keyboard bell when appropriate.
125 	 */
126 	private boolean bellEnabled = true;
127 
128 
129 	/**
130 	 *  The current character mask.
131 	 */
132 	private Character mask = null;
133 
134 
135 	/**
136 	 *  The null mask.
137 	 */
138 	private static final Character NULL_MASK = new Character ((char)0);
139 
140 
141 	/**
142 	 *  The number of tab-completion candidates above which a warning
143 	 *  will be prompted before showing all the candidates.
144 	 */
145 	private int autoprintThreshhold = Integer.getInteger (
146 		"jline.completion.threshold", 100).intValue (); // same default as bash
147 
148 
149 	/**
150 	 *  The Terminal to use.
151 	 */
152 	private final Terminal terminal;
153 
154 
155 	private CompletionHandler completionHandler
156 		= new CandidateListCompletionHandler ();
157 
158 
159 	InputStream in;
160 	final Writer out;
161 	final CursorBuffer buf = new CursorBuffer ();
162 	static PrintWriter debugger;
163 	History history = new History ();
164 	final List completors = new LinkedList ();
165 
166 	private Character echoCharacter = null;
167 
168 
169 	/**
170 	 *  Create a new reader using {@link FileDescriptor#in} for input
171 	 *  and {@link System#out} for output. {@link FileDescriptor#in} is
172 	 *  used because it has a better chance of being unbuffered.
173 	 */
174 	public ConsoleReader ()
175 		throws IOException
176 	{
177 		this (new FileInputStream (FileDescriptor.in),
178 			new PrintWriter (System.out));
179 	}
180 
181 
182 	/**
183 	 *  Create a new reader using the specified {@link InputStream}
184 	 *  for input and the specific writer for output, using the
185 	 *  default keybindings resource.
186 	 */
187 	public ConsoleReader (final InputStream in, final Writer out)
188 		throws IOException
189 	{
190 		this (in, out, null);
191 	}
192 
193 
194 	public ConsoleReader (final InputStream in, final Writer out,
195 		final InputStream bindings)
196 		throws IOException
197 	{
198 		this (in, out, bindings, Terminal.getTerminal ());
199 	}
200 
201 
202 	/**
203 	 *  Create a new reader.
204 	 *
205 	 *  @param  in			the input
206 	 *  @param  out			the output
207 	 *  @param  bindings	the key bindings to use
208 	 *  @param  term		the terminal to use
209 	 */
210 	public ConsoleReader (InputStream in, Writer out, InputStream bindings,
211 		Terminal term)
212 		throws IOException
213 	{
214 		this.terminal = term;
215 		setInput (in);
216 		this.out = out;
217 		if (bindings == null)
218 		{
219 			String bindingFile = System.getProperty ("jline.keybindings",
220 				new File (System.getProperty ("user.home",
221 					".jlinebindings.properties")).getAbsolutePath ());
222 
223 			if (!(new File (bindingFile).isFile ()))
224 				bindings = ConsoleReader.class.getResourceAsStream (
225 					"keybindings.properties");
226 			else
227 				bindings = new FileInputStream (new File (bindingFile));
228 		}
229 
230 		this.keybindings = new short[Byte.MAX_VALUE * 2];
231 
232 		Arrays.fill (this.keybindings, UNKNOWN);
233 
234 		/**
235 		 *	Loads the key bindings. Bindings file is in the format:
236 		 *
237 		 *	keycode: operation name
238 		 */
239 		if (bindings != null)
240 		{
241 			Properties p = new Properties ();
242 			p.load (bindings);
243 			bindings.close ();
244 
245 			for (Iterator i = p.keySet ().iterator (); i.hasNext (); )
246 			{
247 				String val = (String)i.next ();
248 				try
249 				{
250 					Short code = new Short (val);
251 					String op = (String)p.getProperty (val);
252 
253 					Short opval = (Short)KEYMAP_NAMES.get (op);
254 
255 					if (opval != null)
256 						keybindings[code.shortValue ()] = opval.shortValue ();
257 				}
258 				catch (NumberFormatException nfe)
259 				{
260 					consumeException (nfe);
261 				}
262 			}
263 
264 			// hardwired arrow key bindings
265 			// keybindings[VK_UP] = PREV_HISTORY;
266 			// keybindings[VK_DOWN] = NEXT_HISTORY;
267 			// keybindings[VK_LEFT] = PREV_CHAR;
268 			// keybindings[VK_RIGHT] = NEXT_CHAR;
269 		}
270 	}
271 
272 
273 	public Terminal getTerminal ()
274 	{
275 		return this.terminal;
276 	}
277 
278 
279 
280 	/**
281 	 *  Set the stream for debugging. Development use only.
282 	 */
283 	public void setDebug (final PrintWriter debugger)
284 	{
285 		ConsoleReader.debugger = debugger;
286 	}
287 
288 
289 	/**
290 	 *  Set the stream to be used for console input.
291 	 */
292 	public void setInput (final InputStream in)
293 	{
294 		this.in = in;
295 	}
296 
297 
298 	/**
299 	 *  Returns the stream used for console input.
300 	 */
301 	public InputStream getInput ()
302 	{
303 		return this.in;
304 	}
305 
306 
307 	/**
308 	 *  Read the next line and return the contents of the buffer.
309 	 */
310 	public String readLine ()
311 		throws IOException
312 	{
313 		return readLine ((String)null);
314 	}
315 
316 
317 	/**
318 	 *  Read the next line with the specified character mask. If null, then
319 	 *  characters will be echoed. If 0, then no characters will be echoed.
320 	 */
321 	public String readLine (final Character mask)
322 		throws IOException
323 	{
324 		return readLine (null, mask);
325 	}
326 
327 
328 	/**
329 	 *  @param  bellEnabled  if true, enable audible keyboard bells if
330 	 *  					an alert is required.
331 	 */
332 	public void setBellEnabled (final boolean bellEnabled)
333 	{
334 		this.bellEnabled = bellEnabled;
335 	}
336 
337 
338 	/**
339 	 *  @return  true is audible keyboard bell is enabled.
340 	 */
341 	public boolean getBellEnabled ()
342 	{
343 		return this.bellEnabled;
344 	}
345 
346 
347 	/**
348 	 *	Query the terminal to find the current width;
349 	 *
350 	 *	@see	 Terminal#getTerminalWidth
351 	 *  @return  the width of the current terminal.
352 	 */
353 	public int getTermwidth ()
354 	{
355 		return Terminal.setupTerminal ().getTerminalWidth ();
356 	}
357 
358 
359 	/**
360 	 *	Query the terminal to find the current width;
361 	 *
362 	 *	@see	 Terminal#getTerminalHeight
363 	 *
364 	 *  @return  the height of the current terminal.
365 	 */
366 	public int getTermheight ()
367 	{
368 		return Terminal.setupTerminal ().getTerminalHeight ();
369 	}
370 
371 
372 	/**
373 	 *  @param  autoprintThreshhold  the number of candidates to print
374 	 *  							without issuing a warning.
375 	 */
376 	public void setAutoprintThreshhold (final int autoprintThreshhold)
377 	{
378 		this.autoprintThreshhold = autoprintThreshhold;
379 	}
380 
381 
382 	/**
383 	 *  @return  the number of candidates to print without issing a warning.
384 	 */
385 	public int getAutoprintThreshhold ()
386 	{
387 		return this.autoprintThreshhold;
388 	}
389 
390 
391 	int getKeyForAction (short logicalAction)
392 	{
393 		for (int i = 0; i < keybindings.length; i++)
394 		{
395 			if (keybindings[i] == logicalAction)
396 			{
397 				return i;
398 			}
399 		}
400 
401 		return -1;
402 	}
403 
404 
405 	/**
406 	 *  Clear the echoed characters for the specified character code.
407 	 */
408 	int clearEcho (int c)
409 		throws IOException
410 	{
411 		// if the terminal is not echoing, then just return...
412 		if (!terminal.getEcho ())
413 			return 0;
414 
415 		// otherwise, clear
416 		int num = countEchoCharacters ((char)c);
417 		back (num);
418 		drawBuffer (num);
419 
420 		return num;
421 	}
422 
423 
424 	int countEchoCharacters (char c)
425 	{
426 		// tabs as special: we need to determine the number of spaces
427 		// to cancel based on what out current cursor position is
428 		if (c == 9)
429 		{
430 			int tabstop = 8; // will this ever be different?
431 			int position = getCursorPosition ();
432 			return tabstop - (position % tabstop);
433 		}
434 
435 		return getPrintableCharacters (c).length ();
436 	}
437 
438 
439 	/**
440 	 *  Return the number of characters that will be printed when the
441 	 *  specified character is echoed to the screen. Adapted from
442 	 *	cat by Torbjorn Granlund, as repeated in stty by
443 	 *	David MacKenzie.
444 	 */
445 	StringBuffer getPrintableCharacters (char ch)
446 	{
447 		StringBuffer sbuff = new StringBuffer ();
448 		if (ch >= 32)
449 		{
450 			if (ch < 127)
451 			{
452 				sbuff.append (ch);
453 			}
454 			else if (ch == 127)
455 			{
456 				sbuff.append ('^');
457 				sbuff.append ('?');
458 			}
459 			else
460 			{
461 				sbuff.append ('M');
462 				sbuff.append ('-');
463 				if (ch >= 128 + 32)
464 				{
465 					if (ch < 128 + 127)
466 					{
467 						sbuff.append ((char)(ch - 128));
468 					}
469 					else
470 					{
471 						sbuff.append ('^');
472 						sbuff.append ('?');
473 					}
474 				}
475 				else
476 				{
477 					sbuff.append ('^');
478 					sbuff.append ((char)(ch - 128 + 64));
479 				}
480 			}
481 		}
482 		else
483 		{
484 			sbuff.append ('^');
485 			sbuff.append ((char)(ch + 64));
486 		}
487 
488 		return sbuff;
489 	}
490 
491 
492 	int getCursorPosition ()
493 	{
494 		// FIXME: does not handle anything but a line with a prompt
495 		return (prompt == null ? 0 : prompt.length ())
496 			+ buf.cursor; // absolute position
497 	}
498 
499 
500 	public String readLine (final String prompt)
501 		throws IOException
502 	{
503 		return readLine (prompt, null);
504 	}
505 
506 
507 	/**
508 	 *  Read a line from the <i>in</i> {@link InputStream}, and
509 	 *  return the line (without any trailing newlines).
510 	 *
511 	 *  @param  prompt	the prompt to issue to the console, may be null.
512 	 *  @return	a line that is read from the terminal, or null if there
513 	 *  		was null input (e.g., <i>CTRL-D</i> was pressed).
514 	 */
515 	public String readLine (final String prompt, final Character mask)
516 		throws IOException
517 	{
518 		this.mask = mask;
519 		this.prompt = prompt;
520 
521 		if (prompt != null && prompt.length () > 0)
522 		{
523 			out.write (prompt);
524 			out.flush ();
525 		}
526 
527 		// if the terminal is unsupported, just use plain-java reading
528 		if (!terminal.isSupported ())
529 			return readLine (in);
530 
531 		while (true)
532 		{
533 			int[] next = readBinding ();
534 			if (next == null)
535 				return null;
536 
537 			int c = next[0];
538 			int code = next[1];
539 
540 			if (c == -1)
541 				return null;
542 
543 			boolean success = true;
544 
545 			switch (code)
546 			{
547 				case EXIT: // ctrl-d
548 					if (buf.buffer.length () == 0)
549 						return null;
550 				case COMPLETE: // tab
551 					success = complete ();
552 					break;
553 				case MOVE_TO_BEG:
554 					success = setCursorPosition (0);
555 					break;
556 				case KILL_LINE: // CTRL-K
557 					success = killLine ();
558 					break;
559 				case CLEAR_SCREEN: // CTRL-L
560 					success = clearScreen ();
561 					break;
562 				case KILL_LINE_PREV: // CTRL-U
563 					success = resetLine ();
564 					break;
565 				case NEWLINE: // enter
566 					printNewline (); // output newline
567 					return finishBuffer ();
568 				case DELETE_PREV_CHAR: // backspace
569 					success = backspace ();
570 					break;
571 				case MOVE_TO_END:
572 					success = moveToEnd ();
573 					break;
574 				case PREV_CHAR:
575 					success = moveCursor (-1) != 0;
576 					break;
577 				case NEXT_CHAR:
578 					success = moveCursor (1) != 0;
579 					break;
580 				case NEXT_HISTORY:
581 					success = moveHistory (true);
582 					break;
583 				case PREV_HISTORY:
584 					success = moveHistory (false);
585 					break;
586 				case REDISPLAY:
587 					break;
588 				case PASTE:
589 					success = paste ();
590 					break;
591 				case DELETE_PREV_WORD:
592 					success = deletePreviousWord ();
593 					break;
594 				case PREV_WORD:
595 					success = previousWord ();
596 					break;
597 				case NEXT_WORD:
598 					success = nextWord ();
599 					break;
600 
601 				case UNKNOWN:
602 				default:
603 					putChar (c, true);
604 			}
605 
606 			if (!(success))
607 				beep ();
608 
609 			flushConsole ();
610 		}
611 	}
612 
613 
614 	private String readLine (InputStream in)
615 		throws IOException
616 	{
617 		StringBuffer buf = new StringBuffer ();
618 		while (true)
619 		{
620 			int i = in.read ();
621 			if (i == -1 || i == '\n' || i == '\r')
622 				return buf.toString ();
623 
624 			buf.append ((char)i);
625 		}
626 
627 		// return new BufferedReader (new InputStreamReader (in)).readLine ();
628 	}
629 
630 
631 	/** 
632 	 *  Reads the console input and returns an array of the form
633 	 *  [raw, key binding].
634 	 */
635 	private int[] readBinding ()
636 		throws IOException
637 	{
638 		int c = readVirtualKey ();
639 		if (c == -1)
640 			return null;
641 
642 		// extract the appropriate key binding
643 		short code = keybindings[c];
644 
645 		if (debugger != null)
646 			debug ("    translated: " + (int)c + ": " + code);
647 
648 		return new int[] { c, code };
649 	}
650 
651 
652 	/**
653 	 *  Move up or down the history tree.
654 	 *
655 	 *  @param  direction  less than 0 to move up the tree, down otherwise
656 	 */
657 	private final boolean moveHistory (final boolean next)
658 		throws IOException
659 	{
660 		if (next && !history.next ())
661 		   return false;
662 		else if (!next && !history.previous ())
663 			return false;	
664 
665 		setBuffer (history.current ());
666 		return true;
667 	}
668 
669 
670 	/**
671 	 *  Paste the contents of the clipboard into the console buffer
672 	 *
673 	 *  @return  true if clipboard contents pasted
674 	 */
675 	public boolean paste ()
676 		throws IOException
677 	{
678 		java.awt.datatransfer.Clipboard clipboard
679 			= java.awt.Toolkit.getDefaultToolkit ().getSystemClipboard ();
680 		if (clipboard == null)
681 			return false;
682 
683 		java.awt.datatransfer.Transferable transferable
684 			= clipboard.getContents (null);
685 
686 		if (transferable == null)
687 			return false;
688 
689 		try
690 		{
691 			Object content = transferable.getTransferData (
692 				java.awt.datatransfer.DataFlavor.plainTextFlavor);
693 
694 			/*
695 			 * This fix was suggested in bug #1060649 at
696 			 * http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056
697 			 * to get around the deprecated DataFlavor.plainTextFlavor, but
698 			 * it raises a UnsupportedFlavorException on Mac OS X
699 			Object content = new java.awt.datatransfer.DataFlavor ().
700 				getReaderForText (transferable);
701 			*/
702 
703 			if (content == null)
704 				return false;
705 
706 			String value;
707 
708 			if (content instanceof Reader)
709 			{
710 				// TODO: we might want instead connect to the input stream
711 				// so we can interpret individual lines
712 				value = "";
713 				String line = null;
714 				for (BufferedReader read = new BufferedReader ((Reader)content);
715 					(line = read.readLine ()) != null; )
716 				{
717 					if (value.length () > 0)
718 						value += "\n";
719 
720 					value += line;
721 				}
722 			}
723 			else
724 			{
725 				value = content.toString ();
726 			}
727 
728 
729 			if (value == null)
730 				return true;
731 
732 			putString (value);
733 
734 			return true;
735 		}
736 		catch (java.awt.datatransfer.UnsupportedFlavorException ufe)
737 		{
738 			ufe.printStackTrace ();
739 			return false;
740 		}
741 	}
742 
743 
744 	/**
745 	 *  Kill the buffer ahead of the current cursor position.
746 	 *
747 	 *  @return  true if successful
748 	 */
749 	public boolean killLine ()
750 		throws IOException
751 	{
752 		int cp = buf.cursor;
753 		int len = buf.buffer.length ();
754 		if (cp >= len)
755 			return false;
756 
757 		int num = buf.buffer.length () - cp;
758 		clearAhead (num);
759 		for (int i = 0; i < num; i++)
760 			buf.buffer.deleteCharAt (len - i - 1);
761 		return true;
762 	}
763 
764 
765 	/** 
766 	 *  Clear the screen by issuing the ANSI "clear screen" code.
767 	 */
768 	public boolean clearScreen ()
769 		throws IOException
770 	{
771 		if (!terminal.isANSISupported ())
772 			return false;
773 
774 		// send the ANSI code to clear the screen
775 		printString (((char)27) + "[2J");
776 		flushConsole ();
777 				
778 		// then send the ANSI code to go to position 1,1
779 		printString (((char)27) + "[1;1H");
780 		flushConsole ();
781 		
782 		redrawLine ();
783 
784 		return true;
785 	}
786 
787 
788 	/**
789 	 *  Use the completors to modify the buffer with the
790 	 *  appropriate completions.
791 	 *
792 	 *  @return  true if successful
793 	 */
794 	private final boolean complete ()
795 		throws IOException
796 	{
797 		// debug ("tab for (" + buf + ")");
798 
799 		if (completors.size () == 0)
800 			return false;
801 
802 		List candidates = new LinkedList ();
803 		String bufstr = buf.buffer.toString ();
804 		int cursor = buf.cursor;
805 
806 		int position = -1;
807 
808 		for (Iterator i = completors.iterator (); i.hasNext (); )
809 		{
810 			Completor comp = (Completor)i.next ();
811 			if ((position = comp.complete (bufstr, cursor, candidates)) != -1)
812 				break;
813 		}
814 
815 		// no candidates? Fail.
816 		if (candidates.size () == 0)
817 			return false;
818 
819 		return completionHandler.complete (this, candidates, position);
820 	}
821 
822 
823 	public CursorBuffer getCursorBuffer ()
824 	{
825 		return buf;
826 	}
827 
828 
829 	/**
830 	 *  Output the specified {@link Collection} in proper columns.
831 	 *
832 	 *  @param  stuff  the stuff to print
833 	 */
834 	public void printColumns (final Collection stuff)
835 		throws IOException
836 	{
837 		if (stuff == null || stuff.size () == 0)
838 			return;
839 
840 		int width = getTermwidth ();
841 		int maxwidth = 0;
842 		for (Iterator i = stuff.iterator (); i.hasNext ();
843 			maxwidth = Math.max (maxwidth, i.next ().toString ().length ()));
844 
845 		StringBuffer line = new StringBuffer ();
846 
847 		for (Iterator i = stuff.iterator (); i.hasNext (); )
848 		{
849 			String cur = (String)i.next ();
850 
851 			if (line.length () + maxwidth > width)
852 			{
853 				printString (line.toString ().trim ());
854 				printNewline ();
855 				line.setLength (0);
856 			}
857 
858 			pad (cur, maxwidth + 3, line);
859 		}
860 
861 		if (line.length () > 0)
862 		{
863 			printString (line.toString ().trim ());
864 			printNewline ();
865 			line.setLength (0);
866 		}
867 	}
868 
869 
870 	/**
871 	 *  Append <i>toPad</i> to the specified <i>appendTo</i>, as
872 	 *  well as (<i>toPad.length () - len</i>) spaces.
873 	 *
874 	 *  @param  toPad		the {@link String} to pad
875 	 *  @param  len			the target length
876 	 *  @param  appendTo	the {@link StringBuffer} to which to append the
877 	 *  					padded {@link String}.
878 	 */
879 	private final void pad (final String toPad,
880 		final int len, final StringBuffer appendTo)
881 	{
882 		appendTo.append (toPad);
883 		for (int i = 0; i < (len - toPad.length ());
884 			i++, appendTo.append (' '));
885 	}
886 
887 
888 	/**
889 	 *  Add the specified {@link Completor} to the list of handlers
890 	 *  for tab-completion.
891 	 *
892 	 *  @param  completor  the {@link Completor} to add
893 	 *  @return	true if it was successfully added
894 	 */
895 	public boolean addCompletor (final Completor completor)
896 	{
897 		return completors.add (completor);
898 	}
899 
900 
901 	/**
902 	 *  Remove the specified {@link Completor} from the list of handlers
903 	 *  for tab-completion.
904 	 *
905 	 *  @param  completor  the {@link Completor} to remove
906 	 *  @return	true if it was successfully removed
907 	 */
908 	public boolean removeCompletor (final Completor completor)
909 	{
910 		return completors.remove (completor);
911 	}
912 
913 
914 	/**
915 	 *  Returns an unmodifiable list of all the completors.
916 	 */
917 	public Collection getCompletors ()
918 	{
919 		return Collections.unmodifiableList (completors);
920 	}
921 
922 
923 	/**
924 	 *  Erase the current line.
925 	 *
926 	 *  @return  false if we failed (e.g., the buffer was empty)
927 	 */
928 	final boolean resetLine ()
929 		throws IOException
930 	{
931 		if (buf.cursor == 0)
932 			return false;
933 
934 		backspaceAll ();
935 
936 		return true;
937 	}
938 
939 
940 	/**
941 	 *  Move the cursor position to the specified absolute index.
942 	 */
943 	public final boolean setCursorPosition (final int position)
944 		throws IOException
945 	{
946 		return moveCursor (position - buf.cursor) != 0;
947 	}
948 
949 
950 	/**
951 	 *  Set the current buffer's content to the specified
952 	 *  {@link String}. The visual console will be modified
953 	 *  to show the current buffer.
954 	 *
955 	 *  @param  buffer  the new contents of the buffer.
956 	 */
957 	private final void setBuffer (final String buffer)
958 		throws IOException
959 	{
960 		// don't bother modifying it if it is unchanged
961 		if (buffer.equals (buf.buffer.toString ()))
962 			return;
963 
964 		// obtain the difference between the current buffer and the new one
965 		int sameIndex = 0;
966 		for (int i = 0, l1 = buffer.length (), l2 = buf.buffer.length ();
967 			i < l1 && i < l2; i++)
968 		{
969 			if (buffer.charAt (i) == buf.buffer.charAt (i))
970 				sameIndex++;
971 			else
972 				break;
973 		}
974 
975 		int diff = buf.buffer.length () - sameIndex;
976 
977 		backspace (diff); // go back for the differences
978 		killLine (); // clear to the end of the line
979 		buf.buffer.setLength (sameIndex); // the new length
980 		putString (buffer.substring (sameIndex)); // append the differences
981 	}
982 
983 
984 	/**
985 	 *  Clear the line and redraw it.
986 	 */
987 	public final void redrawLine ()
988 		throws IOException
989 	{
990 		printCharacter (RESET_LINE);
991 		flushConsole ();
992 		drawLine ();
993 	}
994 
995 
996 	/**
997 	 *  Output put the prompt + the current buffer
998 	 */
999 	public final void drawLine ()
1000 		throws IOException
1001 	{
1002 		if (prompt != null)
1003 			printString (prompt);
1004 		printString (buf.buffer.toString ());
1005 	}
1006 
1007 
1008 	/**
1009 	 *  Output a platform-dependant newline.
1010 	 */
1011 	public final void printNewline ()
1012 		throws IOException
1013 	{
1014 		printString (CR);
1015 		flushConsole ();
1016 	}
1017 
1018 
1019 	/**
1020 	 *  Clear the buffer and add its contents to the history.
1021 	 *
1022 	 *  @return  the former contents of the buffer.
1023 	 */
1024 	final String finishBuffer ()
1025 	{
1026 		String str = buf.buffer.toString ();
1027 
1028 		// we only add it to the history if the buffer is not empty
1029 		// and if mask is null, since having a mask typically means
1030 		// the string was a password. We clear the mask after this call
1031 		if (str.length () > 0)
1032 		{
1033 			if (mask == null) 
1034 			{
1035 				history.addToHistory (str);
1036 			}
1037 			else 
1038 			{
1039 				mask = null;
1040 			}
1041 		}
1042 		
1043 		history.moveToEnd ();
1044 
1045 		buf.buffer.setLength (0);
1046 		buf.cursor = 0;
1047 		return str;
1048 	}
1049 
1050 
1051 	/**
1052 	 *  Write out the specified string to the buffer and the
1053 	 *  output stream.
1054 	 */
1055 	public final void putString (final String str)
1056 		throws IOException
1057 	{
1058 		buf.insert (str);
1059 		printString (str);
1060 		drawBuffer ();
1061 	}
1062 
1063 
1064 	/**
1065 	 *  Output the specified string to the output stream (but not the
1066 	 *  buffer).
1067 	 */
1068 	public final void printString (final String str)
1069 		throws IOException
1070 	{
1071 		printCharacters (str.toCharArray ());
1072 	}
1073 
1074 
1075 	/**
1076 	 *  Output the specified character, both to the buffer
1077 	 *  and the output stream.
1078 	 */
1079 	private final void putChar (final int c, final boolean print)
1080 		throws IOException
1081 	{
1082 		buf.insert ((char)c);
1083 
1084 		if (print)
1085 		{
1086 			// no masking...
1087 			if (mask == null)
1088 			{
1089 				printCharacter (c);
1090 			}
1091 			// null mask: don't print anything...
1092 			else if (mask.charValue () == 0);
1093 			// otherwise print the mask...
1094 			else
1095 			{
1096 				printCharacter (mask.charValue ());
1097 			}
1098 			drawBuffer ();
1099 		}
1100 	}
1101 
1102 
1103 	/**
1104 	 *  Redraw the rest of the buffer from the cursor onwards. This
1105 	 *  is necessary for inserting text into the buffer.
1106 	 *
1107 	 *  @param clear	the number of characters to clear after the
1108 	 *  				end of the buffer
1109 	 */
1110 	private final void drawBuffer (final int clear)
1111 		throws IOException
1112 	{
1113 		// debug ("drawBuffer: " + clear);
1114 
1115 		char[] chars = buf.buffer.substring (buf.cursor).toCharArray ();
1116 		printCharacters (chars);
1117 
1118 		clearAhead (clear);
1119 		back (chars.length);
1120 		flushConsole ();
1121 	}
1122 
1123 
1124 	/**
1125 	 *  Redraw the rest of the buffer from the cursor onwards. This
1126 	 *  is necessary for inserting text into the buffer.
1127 	 */
1128 	private final void drawBuffer ()
1129 		throws IOException
1130 	{
1131 		drawBuffer (0);
1132 	}
1133 
1134 
1135 	/**
1136 	 *  Clear ahead the specified number of characters
1137 	 *  without moving the cursor.
1138 	 */
1139 	private final void clearAhead (final int num)
1140 		throws IOException
1141 	{
1142 		if (num == 0)
1143 			return;
1144 
1145 		// debug ("clearAhead: " + num);
1146 
1147 		// print blank extra characters
1148 		printCharacters (' ', num);
1149 
1150 		// we need to flush here so a "clever" console
1151 		// doesn't just ignore the redundancy of a space followed by
1152 		// a backspace.
1153 		flushConsole ();
1154 
1155 		// reset the visual cursor
1156 		back (num);
1157 
1158 		flushConsole ();
1159 	}
1160 
1161 
1162 	/**
1163 	 *  Move the visual cursor backwards without modifying the
1164 	 *  buffer cursor.
1165 	 */
1166 	private final void back (final int num)
1167 		throws IOException
1168 	{
1169 		printCharacters (BACKSPACE, num);
1170 		flushConsole ();
1171 	}
1172 
1173 
1174 	/**
1175 	 *  Issue an audible keyboard bell, if
1176 	 *  {@link #getBellEnabled} return true.
1177 	 */
1178 	public final void beep ()
1179 		throws IOException
1180 	{
1181 		if (!(getBellEnabled ()))
1182 			return;
1183 
1184 		printCharacter (KEYBOARD_BELL);
1185 		// need to flush so the console actually beeps
1186 		flushConsole ();
1187 	}
1188 
1189 
1190 	/**
1191 	 *  Output the specified character to the output stream
1192 	 *  without manipulating the current buffer.
1193 	 */
1194 	private final void printCharacter (final int c)
1195 		throws IOException
1196 	{
1197 		out.write (c);
1198 	}
1199 
1200 
1201 	/**
1202 	 *  Output the specified characters to the output stream
1203 	 *  without manipulating the current buffer.
1204 	 */
1205 	private final void printCharacters (final char[] c)
1206 		throws IOException
1207 	{
1208 		out.write (c);
1209 	}
1210 
1211 
1212 	private final void printCharacters (final char c, final int num)
1213 		throws IOException
1214 	{
1215 		if (num == 1)
1216 		{
1217 			printCharacter (c);
1218 		}
1219 		else
1220 		{
1221 			char[] chars = new char[num];
1222 			Arrays.fill (chars, c);
1223 			printCharacters (chars);
1224 		}
1225 	}
1226 
1227 
1228 	/**
1229 	 *  Flush the console output stream. This is important for
1230 	 *  printout out single characters (like a backspace or keyboard)
1231 	 *  that we want the console to handle immedately.
1232 	 */
1233 	public final void flushConsole ()
1234 		throws IOException
1235 	{
1236 		out.flush ();
1237 	}
1238 
1239 
1240 	private final int backspaceAll ()
1241 		throws IOException
1242 	{
1243 		return backspace (Integer.MAX_VALUE);
1244 	}
1245 
1246 
1247 	/**
1248 	 *  Issue <em>num</em> backspaces.
1249 	 *
1250 	 *  @return  the number of characters backed up
1251 	 */
1252 	private final int backspace (final int num)
1253 		throws IOException
1254 	{
1255 		if (buf.cursor == 0)
1256 			return 0;
1257 
1258 		int count = 0;
1259 
1260 		count = moveCursor (-1 * num) * -1;
1261 		// debug ("Deleting from " + buf.cursor + " for " + count);
1262 
1263 		buf.buffer.delete (buf.cursor, buf.cursor + count);
1264 		drawBuffer (count);
1265 
1266 		return count;
1267 	}
1268 
1269 
1270 	/**
1271 	 *  Issue a backspace.
1272 	 *
1273 	 *  @return  true if successful
1274 	 */
1275 	public final boolean backspace ()
1276 		throws IOException
1277 	{
1278 		return backspace (1) == 1;
1279 	}
1280 
1281 
1282 	private final boolean moveToEnd ()
1283 		throws IOException
1284 	{
1285 		if (moveCursor (1) == 0)
1286 			return false;
1287 
1288 		while (moveCursor (1) != 0);
1289 
1290 		return true;
1291 	}
1292 
1293 
1294 	/**
1295 	 *  Delete the character at the current position and
1296 	 *  redraw the remainder of the buffer.
1297 	 */
1298 	private final boolean deleteCurrentCharacter ()
1299 		throws IOException
1300 	{
1301 		buf.buffer.deleteCharAt (buf.cursor);
1302 		drawBuffer (1);
1303 		return true;
1304 	}
1305 
1306 
1307 	private final boolean previousWord ()
1308 		throws IOException
1309 	{
1310 		while (isDelimiter (buf.current ()) && moveCursor (-1) != 0);
1311 		while (!isDelimiter (buf.current ()) && moveCursor (-1) != 0);
1312 
1313 		return true;
1314 	}
1315 
1316 
1317 	private final boolean nextWord ()
1318 		throws IOException
1319 	{
1320 		while (isDelimiter (buf.current ()) && moveCursor (1) != 0);
1321 		while (!isDelimiter (buf.current ()) && moveCursor (1) != 0);
1322 
1323 		return true;
1324 	}
1325 
1326 
1327 	private final boolean deletePreviousWord ()
1328 		throws IOException
1329 	{
1330 		while (isDelimiter (buf.current ()) && backspace ());
1331 		while (!isDelimiter (buf.current ()) && backspace ());
1332 
1333 		return true;
1334 	}
1335 
1336 
1337 	/**
1338 	 *  Move the cursor <i>where</i> characters.
1339 	 *
1340 	 *  @param  where  if less than 0, move abs(<i>where</i>) to the left,
1341 	 *  				otherwise move <i>where</i> to the right.
1342 	 *
1343 	 *  @return  the number of spaces we moved
1344 	 */
1345 	private final int moveCursor (final int num)
1346 		throws IOException
1347 	{
1348 		int where = num;
1349 		if (buf.cursor == 0 && where < 0)
1350 			return 0;
1351 
1352 		if (buf.cursor == buf.buffer.length () && where > 0)
1353 			return 0;
1354 
1355 		if (buf.cursor + where < 0)
1356 			where = -buf.cursor;
1357 		else if (buf.cursor + where > buf.buffer.length ())
1358 			where = buf.buffer.length () - buf.cursor;
1359 
1360 		moveInternal (where);
1361 		return where;
1362 	}
1363 
1364 
1365 	/**
1366 	 *  debug.
1367 	 *
1368 	 *  @param  str  the message to issue.
1369 	 */
1370 	public static void debug (final String str)
1371 	{
1372 		if (debugger != null)
1373 		{
1374 			debugger.println (str);
1375 			debugger.flush ();
1376 		}
1377 	}
1378 
1379 
1380 	/**
1381 	 *  Move the cursor <i>where</i> characters, withough checking
1382 	 *  the current buffer.
1383 	 *
1384 	 *  @see	#where
1385 	 *
1386 	 *  @param  where  the number of characters to move to the right or left.
1387 	 */
1388 	private final void moveInternal (final int where)
1389 		throws IOException
1390 	{
1391 		// debug ("move cursor " + where + " ("
1392 			// + buf.cursor + " => " + (buf.cursor + where) + ")");
1393 
1394 		buf.cursor += where;
1395 
1396 		char c;
1397 
1398 		if (where < 0)
1399 		{
1400 			c = BACKSPACE;
1401 		}
1402 		else if (buf.cursor == 0)
1403 		{
1404 			return;
1405 		}
1406 		else
1407 		{
1408 			c = buf.buffer.charAt (buf.cursor - 1); // draw replacement
1409 		}
1410 
1411 		// null character mask: don't output anything
1412 		if (NULL_MASK.equals (mask))
1413 			return;
1414 
1415 		printCharacters (c, Math.abs (where));
1416 	}
1417 
1418 
1419 	/**
1420 	 *  Read a character from the console.
1421 	 *
1422 	 *  @return  the character, or -1 if an EOF is received.
1423 	 */
1424 	public final int readVirtualKey ()
1425 		throws IOException
1426 	{
1427 		int c = terminal.readVirtualKey (in);
1428 
1429 		if (debugger != null)
1430 			debug ("keystroke: " + c + "");
1431 
1432 		// clear any echo characters
1433 		clearEcho (c);
1434 
1435 		return c;
1436 	}
1437 
1438 
1439 	public final int readCharacter (final char[] allowed)
1440 		throws IOException
1441 	{
1442 
1443 		// if we restrict to a limited set and the current character
1444 		// is not in the set, then try again.
1445 		char c;
1446 
1447 		Arrays.sort (allowed); // always need to sort before binarySearch
1448 		while (Arrays.binarySearch (allowed,
1449 			c = (char)readVirtualKey ()) == -1);
1450 
1451 		return c;
1452 	}
1453 
1454 
1455 	public void setHistory (final History history)
1456 	{
1457 		this.history = history;
1458 	}
1459 
1460 
1461 	public History getHistory ()
1462 	{
1463 		return this.history;
1464 	}
1465 
1466 
1467 	public void setCompletionHandler (final CompletionHandler completionHandler)
1468 	{
1469 		this.completionHandler = completionHandler;
1470 	}
1471 
1472 
1473 	public CompletionHandler getCompletionHandler ()
1474 	{
1475 		return this.completionHandler;
1476 	}
1477 
1478 
1479 
1480 	/**
1481 	 *	<p>
1482 	 *  Set the echo character. For example, to have "*" entered
1483 	 *  when a password is typed:
1484 	 *  </p>
1485 	 *
1486 	 *	<pre>
1487 	 *    myConsoleReader.setEchoCharacter (new Character ('*'));
1488 	 *	</pre>
1489 	 *
1490 	 *	<p>
1491 	 *	Setting the character to <pre>null</pre> will restore normal
1492 	 *	character echoing. Setting the character to
1493 	 *	<pre>new Character (0)</pre> will cause nothing to be echoed.
1494 	 *	</p>
1495 	 *
1496 	 *  @param  echoCharacter	the character to echo to the console in
1497 	 *  						place of the typed character.
1498 	 */
1499 	public void setEchoCharacter (final Character echoCharacter)
1500 	{
1501 		this.echoCharacter = echoCharacter;
1502 	}
1503 
1504 
1505 	/**
1506 	 *  Returns the echo character.
1507 	 */
1508 	public Character getEchoCharacter ()
1509 	{
1510 		return this.echoCharacter;
1511 	}
1512 
1513 
1514 	/** 
1515 	 *  No-op for exceptions we want to silently consume.
1516 	 */
1517 	private void consumeException (final Throwable e)
1518 	{
1519 	}
1520 
1521 
1522 	/** 
1523 	 *  Checks to see if the specified character is a delimiter. We
1524 	 *  consider a character a delimiter if it is anything but a letter or
1525 	 *  digit.
1526 	 *  
1527 	 *  @param  c	the character to test
1528 	 *  @return		true if it is a delimiter
1529 	 */
1530 	private boolean isDelimiter (char c)
1531 	{
1532 		return !Character.isLetterOrDigit (c);
1533 	}
1534 }
1535