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 }