001 /**
002 * jline - Java console input library
003 * Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or
007 * without modification, are permitted provided that the following
008 * conditions are met:
009 *
010 * Redistributions of source code must retain the above copyright
011 * notice, this list of conditions and the following disclaimer.
012 *
013 * Redistributions in binary form must reproduce the above copyright
014 * notice, this list of conditions and the following disclaimer
015 * in the documentation and/or other materials provided with
016 * the distribution.
017 *
018 * Neither the name of JLine nor the names of its contributors
019 * may be used to endorse or promote products derived from this
020 * software without specific prior written permission.
021 *
022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
024 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
025 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
026 * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
027 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
028 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
029 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
030 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
031 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
032 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
033 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
034 * OF THE POSSIBILITY OF SUCH DAMAGE.
035 */
036 package jline;
037
038 import java.io.*;
039 import java.util.*;
040
041
042 /**
043 * <p>
044 * A simple {@link Completor} implementation that handles a pre-defined
045 * list of completion words.
046 * </p>
047 *
048 * <p>
049 * Example usage:
050 * </p>
051 * <pre>
052 * myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" }));
053 * </pre>
054 *
055 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
056 */
057 public class SimpleCompletor
058 implements Completor, Cloneable
059 {
060 /**
061 * The list of candidates that will be completed.
062 */
063 SortedSet candidates;
064
065
066 /**
067 * A delimiter to use to qualify completions.
068 */
069 String delimiter;
070
071 final SimpleCompletorFilter filter;
072
073
074 /**
075 * Create a new SimpleCompletor with a single possible completion
076 * values.
077 */
078 public SimpleCompletor (final String candidateString)
079 {
080 this (new String [] { candidateString });
081 }
082
083
084 /**
085 * Create a new SimpleCompletor with a list of possible completion
086 * values.
087 */
088 public SimpleCompletor (final String [] candidateStrings)
089 {
090 this (candidateStrings, null);
091 }
092
093
094 public SimpleCompletor (final String[] strings,
095 final SimpleCompletorFilter filter)
096 {
097 this.filter = filter;
098 setCandidateStrings (strings);
099 }
100
101
102 /**
103 * Complete candidates using the contents of the specified Reader.
104 */
105 public SimpleCompletor (final Reader reader)
106 throws IOException
107 {
108 this (getStrings (reader));
109 }
110
111
112 /**
113 * Complete candidates using the whitespearated values in
114 * read from the specified Reader.
115 */
116 public SimpleCompletor (final InputStream in)
117 throws IOException
118 {
119 this (getStrings (new InputStreamReader (in)));
120 }
121
122
123 private static String [] getStrings (final Reader in)
124 throws IOException
125 {
126 final Reader reader = in instanceof BufferedReader
127 ? in
128 : new BufferedReader (in);
129
130 List words = new LinkedList ();
131 String line;
132 while ((line = ((BufferedReader)reader).readLine ()) != null)
133 {
134 for (StringTokenizer tok = new StringTokenizer (line);
135 tok.hasMoreTokens (); words.add (tok.nextToken ()));
136 }
137
138 return (String [])words.toArray (new String [words.size ()]);
139 }
140
141
142 public int complete (final String buffer, final int cursor,
143 final List clist)
144 {
145 String start = buffer == null ? "" : buffer;
146
147 SortedSet matches = candidates.tailSet (start);
148 for (Iterator i = matches.iterator (); i.hasNext (); )
149 {
150 String can = (String)i.next ();
151 if (!(can.startsWith (start)))
152 break;
153
154 if (delimiter != null)
155 {
156 int index = can.indexOf (delimiter, cursor);
157 if (index != -1)
158 can = can.substring (0, index + 1);
159 }
160 clist.add (can);
161 }
162
163 if (clist.size () == 1)
164 clist.set (0, ((String)clist.get (0)) + " ");
165
166 // the index of the completion is always from the beginning of
167 // the buffer.
168 return clist.size () == 0 ? -1 : 0;
169 }
170
171
172 public void setDelimiter (final String delimiter)
173 {
174 this.delimiter = delimiter;
175 }
176
177
178 public String getDelimiter ()
179 {
180 return this.delimiter;
181 }
182
183
184
185 public void setCandidates (final SortedSet candidates)
186 {
187 if (filter != null)
188 {
189 TreeSet filtered = new TreeSet ();
190 for (Iterator i = candidates.iterator (); i.hasNext (); )
191 {
192 String element = (String)i.next ();
193 element = filter.filter (element);
194 if (element != null)
195 filtered.add (element);
196 }
197
198 this.candidates = filtered;
199 }
200 else
201 {
202 this.candidates = candidates;
203 }
204 }
205
206
207 public SortedSet getCandidates ()
208 {
209 return Collections.unmodifiableSortedSet (this.candidates);
210 }
211
212
213 public void setCandidateStrings (final String[] strings)
214 {
215 setCandidates (new TreeSet (Arrays.asList (strings)));
216 }
217
218
219 public void addCandidateString (final String candidateString)
220 {
221 final String string = filter == null
222 ? candidateString
223 : filter.filter (candidateString);
224
225 if (string != null)
226 candidates.add (string);
227 }
228
229
230 public Object clone ()
231 throws CloneNotSupportedException
232 {
233 return super.clone ();
234 }
235
236
237 /**
238 * Filter for elements in the completor.
239 *
240 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
241 */
242 public static interface SimpleCompletorFilter
243 {
244 /**
245 * Filter the specified String. To not filter it, return the
246 * same String as the parameter. To exclude it, return null.
247 */
248 public String filter (String element);
249 }
250
251
252 public static class NoOpFilter
253 implements SimpleCompletorFilter
254 {
255 public String filter (final String element)
256 {
257 return element;
258 }
259 }
260 }