1   
2   
3   
4   
5   
6   
7   
8   
9   package jline.console.completer;
10  
11  import jline.console.ConsoleReader;
12  import jline.console.CursorBuffer;
13  
14  import java.io.IOException;
15  import java.util.ArrayList;
16  import java.util.Collection;
17  import java.util.HashSet;
18  import java.util.List;
19  import java.util.Locale;
20  import java.util.ResourceBundle;
21  import java.util.Set;
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  public class CandidateListCompletionHandler
34      implements CompletionHandler
35  {
36      
37  
38      public boolean complete(final ConsoleReader reader, final List<CharSequence> candidates, final int pos) throws
39          IOException
40      {
41          CursorBuffer buf = reader.getCursorBuffer();
42  
43          
44          if (candidates.size() == 1) {
45              CharSequence value = candidates.get(0);
46  
47              
48              if (value.equals(buf.toString())) {
49                  return false;
50              }
51  
52              setBuffer(reader, value, pos);
53  
54              return true;
55          }
56          else if (candidates.size() > 1) {
57              String value = getUnambiguousCompletions(candidates);
58              setBuffer(reader, value, pos);
59          }
60  
61          printCandidates(reader, candidates);
62  
63          
64          reader.drawLine();
65  
66          return true;
67      }
68  
69      public static void setBuffer(final ConsoleReader reader, final CharSequence value, final int offset) throws
70          IOException
71      {
72          while ((reader.getCursorBuffer().cursor > offset) && reader.backspace()) {
73              
74          }
75  
76          reader.putString(value);
77          reader.setCursorPosition(offset + value.length());
78      }
79  
80      
81  
82  
83  
84  
85  
86      public static void printCandidates(final ConsoleReader reader, Collection<CharSequence> candidates) throws
87          IOException
88      {
89          Set<CharSequence> distinct = new HashSet<CharSequence>(candidates);
90  
91          if (distinct.size() > reader.getAutoprintThreshold()) {
92              
93              reader.print(Messages.DISPLAY_CANDIDATES.format(candidates.size()));
94              reader.flush();
95  
96              int c;
97  
98              String noOpt = Messages.DISPLAY_CANDIDATES_NO.format();
99              String yesOpt = Messages.DISPLAY_CANDIDATES_YES.format();
100             char[] allowed = {yesOpt.charAt(0), noOpt.charAt(0)};
101 
102             while ((c = reader.readCharacter(allowed)) != -1) {
103                 String tmp = new String(new char[]{(char) c});
104 
105                 if (noOpt.startsWith(tmp)) {
106                     reader.println();
107                     return;
108                 }
109                 else if (yesOpt.startsWith(tmp)) {
110                     break;
111                 }
112                 else {
113                     reader.beep();
114                 }
115             }
116         }
117 
118         
119         if (distinct.size() != candidates.size()) {
120             Collection<CharSequence> copy = new ArrayList<CharSequence>();
121 
122             for (CharSequence next : candidates) {
123                 if (!copy.contains(next)) {
124                     copy.add(next);
125                 }
126             }
127 
128             candidates = copy;
129         }
130 
131         reader.println();
132         reader.printColumns(candidates);
133     }
134 
135     
136 
137 
138 
139 
140     private String getUnambiguousCompletions(final List<CharSequence> candidates) {
141         if (candidates == null || candidates.isEmpty()) {
142             return null;
143         }
144 
145         
146         String[] strings = candidates.toArray(new String[candidates.size()]);
147 
148         String first = strings[0];
149         StringBuilder candidate = new StringBuilder();
150 
151         for (int i = 0; i < first.length(); i++) {
152             if (startsWith(first.substring(0, i + 1), strings)) {
153                 candidate.append(first.charAt(i));
154             }
155             else {
156                 break;
157             }
158         }
159 
160         return candidate.toString();
161     }
162 
163     
164 
165 
166     private boolean startsWith(final String starts, final String[] candidates) {
167         for (String candidate : candidates) {
168             if (!candidate.startsWith(starts)) {
169                 return false;
170             }
171         }
172 
173         return true;
174     }
175 
176     private static enum Messages
177     {
178         DISPLAY_CANDIDATES,
179         DISPLAY_CANDIDATES_YES,
180         DISPLAY_CANDIDATES_NO,;
181 
182         private static final
183         ResourceBundle
184             bundle =
185             ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName(), Locale.getDefault());
186 
187         public String format(final Object... args) {
188             if (bundle == null)
189                 return "";
190             else
191                 return String.format(bundle.getString(name()), args);
192         }
193     }
194 }