View Javadoc

1   /*
2    * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
3    *
4    * This software is distributable under the BSD license. See the terms of the
5    * BSD license in the documentation provided with this software.
6    */
7   package jline;
8   
9   import java.io.*;
10  import java.util.*;
11  
12  /***
13   *  <p>
14   *  A simple {@link Completor} implementation that handles a pre-defined
15   *  list of completion words.
16   *  </p>
17   *
18   *  <p>
19   *  Example usage:
20   *  </p>
21   *  <pre>
22   *  myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" }));
23   *  </pre>
24   *
25   *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
26   */
27  public class SimpleCompletor implements Completor, Cloneable {
28      /***
29       *  The list of candidates that will be completed.
30       */
31      SortedSet candidates;
32  
33      /***
34       *  A delimiter to use to qualify completions.
35       */
36      String delimiter;
37      final SimpleCompletorFilter filter;
38  
39      /***
40       *  Create a new SimpleCompletor with a single possible completion
41       *  values.
42       */
43      public SimpleCompletor(final String candidateString) {
44          this(new String[] {
45                   candidateString
46               });
47      }
48  
49      /***
50       *  Create a new SimpleCompletor with a list of possible completion
51       *  values.
52       */
53      public SimpleCompletor(final String[] candidateStrings) {
54          this(candidateStrings, null);
55      }
56  
57      public SimpleCompletor(final String[] strings,
58                             final SimpleCompletorFilter filter) {
59          this.filter = filter;
60          setCandidateStrings(strings);
61      }
62  
63      /***
64       *  Complete candidates using the contents of the specified Reader.
65       */
66      public SimpleCompletor(final Reader reader) throws IOException {
67          this(getStrings(reader));
68      }
69  
70      /***
71       *  Complete candidates using the whitespearated values in
72       *  read from the specified Reader.
73       */
74      public SimpleCompletor(final InputStream in) throws IOException {
75          this(getStrings(new InputStreamReader(in)));
76      }
77  
78      private static String[] getStrings(final Reader in)
79                                  throws IOException {
80          final Reader reader =
81              (in instanceof BufferedReader) ? in : new BufferedReader(in);
82  
83          List words = new LinkedList();
84          String line;
85  
86          while ((line = ((BufferedReader) reader).readLine()) != null) {
87              for (StringTokenizer tok = new StringTokenizer(line);
88                       tok.hasMoreTokens(); words.add(tok.nextToken())) {
89                  ;
90              }
91          }
92  
93          return (String[]) words.toArray(new String[words.size()]);
94      }
95  
96      public int complete(final String buffer, final int cursor, final List clist) {
97          String start = (buffer == null) ? "" : buffer;
98  
99          SortedSet matches = candidates.tailSet(start);
100 
101         for (Iterator i = matches.iterator(); i.hasNext();) {
102             String can = (String) i.next();
103 
104             if (!(can.startsWith(start))) {
105                 break;
106             }
107 
108             if (delimiter != null) {
109                 int index = can.indexOf(delimiter, cursor);
110 
111                 if (index != -1) {
112                     can = can.substring(0, index + 1);
113                 }
114             }
115 
116             clist.add(can);
117         }
118 
119         if (clist.size() == 1) {
120             clist.set(0, ((String) clist.get(0)) + " ");
121         }
122 
123         // the index of the completion is always from the beginning of
124         // the buffer.
125         return (clist.size() == 0) ? (-1) : 0;
126     }
127 
128     public void setDelimiter(final String delimiter) {
129         this.delimiter = delimiter;
130     }
131 
132     public String getDelimiter() {
133         return this.delimiter;
134     }
135 
136     public void setCandidates(final SortedSet candidates) {
137         if (filter != null) {
138             TreeSet filtered = new TreeSet();
139 
140             for (Iterator i = candidates.iterator(); i.hasNext();) {
141                 String element = (String) i.next();
142                 element = filter.filter(element);
143 
144                 if (element != null) {
145                     filtered.add(element);
146                 }
147             }
148 
149             this.candidates = filtered;
150         } else {
151             this.candidates = candidates;
152         }
153     }
154 
155     public SortedSet getCandidates() {
156         return Collections.unmodifiableSortedSet(this.candidates);
157     }
158 
159     public void setCandidateStrings(final String[] strings) {
160         setCandidates(new TreeSet(Arrays.asList(strings)));
161     }
162 
163     public void addCandidateString(final String candidateString) {
164         final String string =
165             (filter == null) ? candidateString : filter.filter(candidateString);
166 
167         if (string != null) {
168             candidates.add(string);
169         }
170     }
171 
172     public Object clone() throws CloneNotSupportedException {
173         return super.clone();
174     }
175 
176     /***
177      *  Filter for elements in the completor.
178      *
179      *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
180      */
181     public static interface SimpleCompletorFilter {
182         /***
183          *  Filter the specified String. To not filter it, return the
184          *  same String as the parameter. To exclude it, return null.
185          */
186         public String filter(String element);
187     }
188 
189     public static class NoOpFilter implements SimpleCompletorFilter {
190         public String filter(final String element) {
191             return element;
192         }
193     }
194 }