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
67 if (candidates.size () == 1)
68 {
69 String value = candidates.get (0).toString ();
70
71
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
84
85 if (bufString.length () - pos + 1 != value.length ())
86 return true;
87 }
88
89 reader.printNewline ();
90 printCandidates (reader, candidates);
91
92
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
156
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
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