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    }