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    }