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  import java.text.MessageFormat;
41  
42  /**
43   *	<p>
44   *	A {@link CompletionHandler} that deals with multiple distinct completions
45   *	by outputting the complete list of possibilities to the console. This
46   *	mimics the behavior of the
47   *	<a href="http://www.gnu.org/directory/readline.html">readline</a>
48   *	library.
49   *	</p>
50   *
51   *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
52   */
53  public class CandidateListCompletionHandler
54  	implements CompletionHandler
55  {
56  	private static ResourceBundle loc = ResourceBundle.getBundle (
57  		CandidateListCompletionHandler.class.getName ());
58  
59  
60  	public boolean complete (final ConsoleReader reader,
61  		final List candidates, final int pos)
62  		throws IOException
63  	{
64  		CursorBuffer buf = reader.getCursorBuffer ();
65  
66  		// if there is only one completion, then fill in the buffer
67  		if (candidates.size () == 1)
68  		{
69  			String value = candidates.get (0).toString ();
70  
71  			// fail if the only candidate is the same as the current buffer
72  			if (value.equals (buf.toString ()))
73  				return false;
74  			setBuffer (reader, value, pos);
75  			return true;
76  		}
77  		else if (candidates.size () > 1)
78  		{
79  			String value = getUnambiguousCompletions (candidates);
80  			String bufString = buf.toString ();
81  			setBuffer (reader, value, pos);
82  
83  			// if we have changed the buffer, then just return withough
84  			// printing out all the subsequent candidates
85  			if (bufString.length () - pos + 1 != value.length ())
86  				return true;
87  		}
88  
89  		reader.printNewline ();
90  		printCandidates (reader, candidates);
91  
92  		// redraw the current console buffer
93  		reader.drawLine ();
94  
95  		return true;
96  	}
97  
98  
99  	private static void setBuffer (ConsoleReader reader,
100 		String value, int offset)
101 		throws IOException
102 	{
103 		while (reader.getCursorBuffer ().cursor >= offset
104 			&& reader.backspace ());
105 		reader.putString (value);
106 		reader.setCursorPosition (offset + value.length ());
107 	}
108 
109 
110 	/**
111 	 *  Print out the candidates. If the size of the candidates
112 	 *  is greated than the {@link getAutoprintThreshhold},
113 	 *  they prompt with aq warning.
114 	 *
115 	 *  @param  candidates  the list of candidates to print
116 	 */
117 	private final void printCandidates (ConsoleReader reader,
118 		Collection candidates)
119 		throws IOException
120 	{
121 		Set distinct = new HashSet (candidates);
122 
123 		if (distinct.size () > reader.getAutoprintThreshhold ())
124 		{
125 			reader.printString (MessageFormat.format (
126 				loc.getString ("display-candidates"),
127 				new Object [] { new Integer (candidates.size ()) } ) + " ");
128 
129 			reader.flushConsole ();
130 
131 			int c;
132 			
133 			String noOpt = loc.getString ("display-candidates-no");
134 			String yesOpt = loc.getString ("display-candidates-yes");
135 
136 			while ((c = reader.readCharacter (
137 				new char[] { yesOpt.charAt (0), noOpt.charAt (0) })) != -1)
138 			{
139 				if (noOpt.startsWith (new String (new char[] {(char)c})))
140 				{
141 					reader.printNewline ();
142 					return;
143 				}
144 				else if (yesOpt.startsWith (new String (new char[] {(char)c})))
145 				{
146 					break;
147 				}
148 				else
149 				{
150 					reader.beep ();
151 				}
152 			}
153 		}
154 
155 		// copy the values and make them distinct, without otherwise
156 		// affecting the ordering. Only do it if the sizes differ.
157 		if (distinct.size () != candidates.size ())
158 		{
159 			Collection copy = new ArrayList ();
160 			for (Iterator i = candidates.iterator (); i.hasNext (); )
161 			{
162 				Object next = i.next ();
163 				if (!(copy.contains (next)))
164 					copy.add (next);
165 			}
166 
167 			candidates = copy;
168 		}
169 
170 		reader.printColumns (candidates);
171 	}
172 
173 
174 
175 
176 	/**
177 	 *  Returns a root that matches all the {@link String} elements
178 	 *  of the specified {@link List}, or null if there are
179 	 *  no commalities. For example, if the list contains
180 	 *  <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the
181 	 *  method will return <i>foob</i>.
182 	 */
183 	private final String getUnambiguousCompletions (final List candidates)
184 	{
185 		if (candidates == null || candidates.size () == 0)
186 			return null;
187 
188 		// convert to an array for speed
189 		String [] strings = (String [])candidates.toArray (
190 			new String [candidates.size ()]);
191 
192 		String first = strings [0];
193 		StringBuffer candidate = new StringBuffer ();
194 		for (int i = 0; i < first.length (); i++)
195 		{
196 			if (startsWith (first.substring (0, i + 1), strings))
197 				candidate.append (first.charAt (i));
198 			else
199 				break;
200 		}
201 
202 		return candidate.toString ();
203 	}
204 
205 
206 	/**
207 	 *  @return  true is all the elements of <i>candidates</i>
208 	 *  			start with <i>starts</i>
209 	 */
210 	private final boolean startsWith (final String starts,
211 		final String [] candidates)
212 	{
213 		for (int i = 0; i < candidates.length; i++)
214 		{
215 			if (!candidates [i].startsWith (starts))
216 				return false;
217 		}
218 
219 		return true;
220 	}
221 }
222