001 /**
002 * jline - Java console input library
003 * Copyright (c) 2002,2003 Marc Prud'hommeaux mwp1@cornell.edu
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018 */
019 package jline;
020
021 import java.io.*;
022 import java.util.*;
023
024
025 /**
026 * <p>
027 * A simple {@link Completor} implementation that handles a pre-defined
028 * list of completion words.
029 * </p>
030 *
031 * <p>
032 * Example usage:
033 * </p>
034 * <pre>
035 * myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" }));
036 * </pre>
037 *
038 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
039 */
040 public class SimpleCompletor
041 implements Completor, Cloneable
042 {
043 /**
044 * The list of candidates that will be completed.
045 */
046 SortedSet candidates;
047
048
049 /**
050 * A delimiter to use to qualify completions.
051 */
052 String delimiter;
053
054 final SimpleCompletorFilter filter;
055
056
057 /**
058 * Create a new SimpleCompletor with a single possible completion
059 * values.
060 */
061 public SimpleCompletor (final String candidateString)
062 {
063 this (new String [] { candidateString });
064 }
065
066
067 /**
068 * Create a new SimpleCompletor with a list of possible completion
069 * values.
070 */
071 public SimpleCompletor (final String [] candidateStrings)
072 {
073 this (candidateStrings, null);
074 }
075
076
077 public SimpleCompletor (final String[] strings,
078 final SimpleCompletorFilter filter)
079 {
080 this.filter = filter;
081 setCandidateStrings (strings);
082 }
083
084
085 /**
086 * Complete candidates using the contents of the specified Reader.
087 */
088 public SimpleCompletor (final Reader reader)
089 throws IOException
090 {
091 this (getStrings (reader));
092 }
093
094
095 /**
096 * Complete candidates using the whitespearated values in
097 * read from the specified Reader.
098 */
099 public SimpleCompletor (final InputStream in)
100 throws IOException
101 {
102 this (getStrings (new InputStreamReader (in)));
103 }
104
105
106 private static String [] getStrings (final Reader in)
107 throws IOException
108 {
109 final Reader reader = in instanceof BufferedReader
110 ? in
111 : new BufferedReader (in);
112
113 List words = new LinkedList ();
114 String line;
115 while ((line = ((BufferedReader)reader).readLine ()) != null)
116 {
117 for (StringTokenizer tok = new StringTokenizer (line);
118 tok.hasMoreTokens (); words.add (tok.nextToken ()));
119 }
120
121 return (String [])words.toArray (new String [words.size ()]);
122 }
123
124
125 public int complete (final String buffer, final int cursor,
126 final List clist)
127 {
128 String start = buffer == null ? "" : buffer;
129
130 SortedSet matches = candidates.tailSet (start);
131 for (Iterator i = matches.iterator (); i.hasNext (); )
132 {
133 String can = (String)i.next ();
134 if (!(can.startsWith (start)))
135 break;
136
137 if (delimiter != null)
138 {
139 int index = can.indexOf (delimiter, cursor);
140 if (index != -1)
141 can = can.substring (0, index + 1);
142 }
143 clist.add (can);
144 }
145
146 if (clist.size () == 1)
147 clist.set (0, ((String)clist.get (0)) + " ");
148
149 // the index of the completion is always from the beginning of
150 // the buffer.
151 return clist.size () == 0 ? -1 : 0;
152 }
153
154
155 public void setDelimiter (final String delimiter)
156 {
157 this.delimiter = delimiter;
158 }
159
160
161 public String getDelimiter ()
162 {
163 return this.delimiter;
164 }
165
166
167
168 public void setCandidates (final SortedSet candidates)
169 {
170 if (filter != null)
171 {
172 TreeSet filtered = new TreeSet ();
173 for (Iterator i = candidates.iterator (); i.hasNext (); )
174 {
175 String element = (String)i.next ();
176 element = filter.filter (element);
177 if (element != null)
178 filtered.add (element);
179 }
180
181 this.candidates = filtered;
182 }
183 else
184 {
185 this.candidates = candidates;
186 }
187 }
188
189
190 public SortedSet getCandidates ()
191 {
192 return Collections.unmodifiableSortedSet (this.candidates);
193 }
194
195
196 public void setCandidateStrings (final String[] strings)
197 {
198 setCandidates (new TreeSet (Arrays.asList (strings)));
199 }
200
201
202 public void addCandidateString (final String candidateString)
203 {
204 final String string = filter == null
205 ? candidateString
206 : filter.filter (candidateString);
207
208 if (string != null)
209 candidates.add (string);
210 }
211
212
213 public Object clone ()
214 throws CloneNotSupportedException
215 {
216 return super.clone ();
217 }
218
219
220 /**
221 * Filter for elements in the completor.
222 *
223 * @author <a href="mailto:marc@solarmetric.com">Marc Prud'hommeaux</a>
224 */
225 public static interface SimpleCompletorFilter
226 {
227 /**
228 * Filter the specified String. To not filter it, return the
229 * same String as the parameter. To exclude it, return null.
230 */
231 public String filter (String element);
232 }
233
234
235 public static class NoOpFilter
236 implements SimpleCompletorFilter
237 {
238 public String filter (final String element)
239 {
240 return element;
241 }
242 }
243 }