001 /*
002 * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
003 *
004 * This software is distributable under the BSD license. See the terms of the
005 * BSD license in the documentation provided with this software.
006 */
007 package jline;
008
009 import java.io.*;
010 import java.util.*;
011
012 /**
013 * <p>
014 * A simple {@link Completor} implementation that handles a pre-defined
015 * list of completion words.
016 * </p>
017 *
018 * <p>
019 * Example usage:
020 * </p>
021 * <pre>
022 * myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" }));
023 * </pre>
024 *
025 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
026 */
027 public class SimpleCompletor implements Completor, Cloneable {
028 /**
029 * The list of candidates that will be completed.
030 */
031 SortedSet candidates;
032
033 /**
034 * A delimiter to use to qualify completions.
035 */
036 String delimiter;
037 final SimpleCompletorFilter filter;
038
039 /**
040 * Create a new SimpleCompletor with a single possible completion
041 * values.
042 */
043 public SimpleCompletor(final String candidateString) {
044 this(new String[] {
045 candidateString
046 });
047 }
048
049 /**
050 * Create a new SimpleCompletor with a list of possible completion
051 * values.
052 */
053 public SimpleCompletor(final String[] candidateStrings) {
054 this(candidateStrings, null);
055 }
056
057 public SimpleCompletor(final String[] strings,
058 final SimpleCompletorFilter filter) {
059 this.filter = filter;
060 setCandidateStrings(strings);
061 }
062
063 /**
064 * Complete candidates using the contents of the specified Reader.
065 */
066 public SimpleCompletor(final Reader reader) throws IOException {
067 this(getStrings(reader));
068 }
069
070 /**
071 * Complete candidates using the whitespearated values in
072 * read from the specified Reader.
073 */
074 public SimpleCompletor(final InputStream in) throws IOException {
075 this(getStrings(new InputStreamReader(in)));
076 }
077
078 private static String[] getStrings(final Reader in)
079 throws IOException {
080 final Reader reader =
081 (in instanceof BufferedReader) ? in : new BufferedReader(in);
082
083 List words = new LinkedList();
084 String line;
085
086 while ((line = ((BufferedReader) reader).readLine()) != null) {
087 for (StringTokenizer tok = new StringTokenizer(line);
088 tok.hasMoreTokens(); words.add(tok.nextToken())) {
089 ;
090 }
091 }
092
093 return (String[]) words.toArray(new String[words.size()]);
094 }
095
096 public int complete(final String buffer, final int cursor, final List clist) {
097 String start = (buffer == null) ? "" : buffer;
098
099 SortedSet matches = candidates.tailSet(start);
100
101 for (Iterator i = matches.iterator(); i.hasNext();) {
102 String can = (String) i.next();
103
104 if (!(can.startsWith(start))) {
105 break;
106 }
107
108 if (delimiter != null) {
109 int index = can.indexOf(delimiter, cursor);
110
111 if (index != -1) {
112 can = can.substring(0, index + 1);
113 }
114 }
115
116 clist.add(can);
117 }
118
119 if (clist.size() == 1) {
120 clist.set(0, ((String) clist.get(0)) + " ");
121 }
122
123 // the index of the completion is always from the beginning of
124 // the buffer.
125 return (clist.size() == 0) ? (-1) : 0;
126 }
127
128 public void setDelimiter(final String delimiter) {
129 this.delimiter = delimiter;
130 }
131
132 public String getDelimiter() {
133 return this.delimiter;
134 }
135
136 public void setCandidates(final SortedSet candidates) {
137 if (filter != null) {
138 TreeSet filtered = new TreeSet();
139
140 for (Iterator i = candidates.iterator(); i.hasNext();) {
141 String element = (String) i.next();
142 element = filter.filter(element);
143
144 if (element != null) {
145 filtered.add(element);
146 }
147 }
148
149 this.candidates = filtered;
150 } else {
151 this.candidates = candidates;
152 }
153 }
154
155 public SortedSet getCandidates() {
156 return Collections.unmodifiableSortedSet(this.candidates);
157 }
158
159 public void setCandidateStrings(final String[] strings) {
160 setCandidates(new TreeSet(Arrays.asList(strings)));
161 }
162
163 public void addCandidateString(final String candidateString) {
164 final String string =
165 (filter == null) ? candidateString : filter.filter(candidateString);
166
167 if (string != null) {
168 candidates.add(string);
169 }
170 }
171
172 public Object clone() throws CloneNotSupportedException {
173 return super.clone();
174 }
175
176 /**
177 * Filter for elements in the completor.
178 *
179 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
180 */
181 public static interface SimpleCompletorFilter {
182 /**
183 * Filter the specified String. To not filter it, return the
184 * same String as the parameter. To exclude it, return null.
185 */
186 public String filter(String element);
187 }
188
189 public static class NoOpFilter implements SimpleCompletorFilter {
190 public String filter(final String element) {
191 return element;
192 }
193 }
194 }