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 (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 (String [] candidateStrings) 072 { 073 this (candidateStrings, null); 074 } 075 076 077 public SimpleCompletor (String[] strings, SimpleCompletorFilter filter) 078 { 079 this.filter = filter; 080 setCandidateStrings (strings); 081 } 082 083 084 /** 085 * Complete candidates using the contents of the specified Reader. 086 */ 087 public SimpleCompletor (Reader reader) 088 throws IOException 089 { 090 this (getStrings (reader)); 091 } 092 093 094 /** 095 * Complete candidates using the whitespearated values in 096 * read from the specified Reader. 097 */ 098 public SimpleCompletor (InputStream in) 099 throws IOException 100 { 101 this (getStrings (new InputStreamReader (in))); 102 } 103 104 105 private static String [] getStrings (Reader reader) 106 throws IOException 107 { 108 if (!(reader instanceof BufferedReader)) 109 reader = new BufferedReader (reader); 110 111 List words = new LinkedList (); 112 String line; 113 while ((line = ((BufferedReader)reader).readLine ()) != null) 114 { 115 for (StringTokenizer tok = new StringTokenizer (line); 116 tok.hasMoreTokens (); words.add (tok.nextToken ())); 117 } 118 119 return (String [])words.toArray (new String [words.size ()]); 120 } 121 122 123 public int complete (String buffer, int cursor, List clist) 124 { 125 String start = buffer == null ? "" : buffer; 126 127 SortedSet matches = candidates.tailSet (start); 128 for (Iterator i = matches.iterator (); i.hasNext (); ) 129 { 130 String can = (String)i.next (); 131 if (!(can.startsWith (start))) 132 break; 133 134 if (delimiter != null) 135 { 136 int index = can.indexOf (delimiter, cursor); 137 if (index != -1) 138 can = can.substring (0, index + 1); 139 } 140 clist.add (can); 141 } 142 143 if (clist.size () == 1) 144 clist.set (0, ((String)clist.get (0)) + " "); 145 146 // the index of the completion is always from the beginning of 147 // the buffer. 148 return clist.size () == 0 ? -1 : 0; 149 } 150 151 152 public void setDelimiter (String delimiter) 153 { 154 this.delimiter = delimiter; 155 } 156 157 158 public String getDelimiter () 159 { 160 return this.delimiter; 161 } 162 163 164 165 public void setCandidates (SortedSet candidates) 166 { 167 if (filter != null) 168 { 169 TreeSet filtered = new TreeSet (); 170 for (Iterator i = candidates.iterator (); i.hasNext (); ) 171 { 172 String element = (String)i.next (); 173 element = filter.filter (element); 174 if (element != null) 175 filtered.add (element); 176 } 177 178 candidates = filtered; 179 } 180 181 this.candidates = candidates; 182 } 183 184 185 public SortedSet getCandidates () 186 { 187 return Collections.unmodifiableSortedSet (this.candidates); 188 } 189 190 191 public void setCandidateStrings (String[] strings) 192 { 193 setCandidates (new TreeSet (Arrays.asList (strings))); 194 } 195 196 197 public void addCandidateString (String candidateString) 198 { 199 if (filter != null) 200 candidateString = filter.filter (candidateString); 201 202 if (candidateString != null) 203 candidates.add (candidateString); 204 } 205 206 207 public Object clone () 208 throws CloneNotSupportedException 209 { 210 return super.clone (); 211 } 212 213 214 /** 215 * Filter for elements in the completor. 216 * 217 * @author <a href="mailto:marc@solarmetric.com">Marc Prud'hommeaux</a> 218 */ 219 public static interface SimpleCompletorFilter 220 { 221 /** 222 * Filter the specified String. To not filter it, return the 223 * same String as the parameter. To exclude it, return null. 224 */ 225 public String filter (String element); 226 } 227 228 229 public static class NoOpFilter 230 implements SimpleCompletorFilter 231 { 232 public String filter (String element) 233 { 234 return element; 235 } 236 } 237 }