Jline是一个开源的Java控制台输入类库,最近的项目需要用到这个东东(Zookeeper的命令行用的也是这个),就是实现在命令行输入SQL语句,并解析它。不过它自带的自动补全功能有点弱,无法满足输入SQL时的TAB提示(提示SQL的关键字、表名、字段名之类的)的需求,下面贴出我的实现,本人使用的版本是0.9.94。
要实现TAB提示,需要实现Jline的Completor接口,其核心方法是complete方法,方法原型是这样的,
public int complete(final String buffer, final int cursor, final List clist)
参数解释:String buffer --- 在控制台按下tab键时已经输入的字符串
int cursor --- 在控制台按下tab键时光标的位置
List clist --- 返回的候选词
下面是我的代码
package client.shell;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import jline.Completor;
public class MyCompletor implements Completor, Cloneable {
SortedSet candidates;
public MyCompletor(final String[] candidateStrings) {
candidates = new TreeSet();
for (String string : candidateStrings) {
candidates.add(string);
}
}
public int complete(final String buffer, final int cursor, final List clist) {
String start = (buffer == null) ? "" : buffer;
// 找到字符串中最后一个空格出现的位置
int index = start.lastIndexOf(" ");
// 把最后一个空格后面的词截出来
String tmp = start.substring(index + 1, start.length());
// candidates中存储的词都是按字典序排下来的
// 例如candidates中有如下三个词:insert select where
// 如果tmp是'se',则通过下面的tailSet方法,会返回select和where两个词
SortedSet matches = candidates.tailSet(tmp);
for (Iterator i = matches.iterator(); i.hasNext();) {
String can = (String) i.next();
// 过滤掉不是以tmp这个字符串开头的词
if (!(can.startsWith(tmp))) {
break;
}
clist.add(can);
}
if (clist.size() == 1) {
clist.set(0, ((String) clist.get(0)) + " ");
}
// the index of the completion is always from the beginning of the
// buffer.
// 如果没有从候选词中找到任何匹配的词,则返回-1
// 如果index为-1,即输入的字符串中没有空格,则返回0(起始位置)
// 如果index不是-1,则返回最后一个空格的位置加1
return (clist.size() == 0) ? (-1) : (index == -1 ? 0 : index + 1);
}
public void setCandidates(final SortedSet candidates) {
this.candidates = candidates;
}
public SortedSet getCandidates() {
return Collections.unmodifiableSortedSet(this.candidates);
}
public void setCandidateStrings(final String[] strings) {
setCandidates(new TreeSet(Arrays.asList(strings)));
}
public void addCandidateString(final String candidateString) {
candidates.add(candidateString);
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
不过在使用中还是有几个问题还没解决,比如按HOME和END键没有返回行首或行尾,有时候按方向键上翻历史记录时,导致前面已经输出的多个字符被历史记录覆盖掉了,这两个问题都是用XShell远程连接时才有的,直接用键盘连机器还没有出现这些问题呀。。。如果哪位大神知晓其中缘由,请下方回复,不胜感激。