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    }