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 }