AutoCompleter.java

package edu.jiangxin.apktoolbox.swing.extend.autocomplete;

import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;

class AutoCompleter<E> implements KeyListener {
    private JComboBox<E> comboBox;
    private JTextField textField;
    private ComboBoxModel<E> comboBoxModel;

    public AutoCompleter(JComboBox<E> comboBox) {
        if (comboBox == null) {
            throw new AutoCompleterInitException("comboBox is null");
        }
        this.comboBox = comboBox;
        ComboBoxEditor comboBoxEditor = comboBox.getEditor();
        if (comboBoxEditor == null) {
            throw new AutoCompleterInitException("comboBoxEditor is null");
        }
        Component component = comboBoxEditor.getEditorComponent();
        if (!(component instanceof JTextField)) {
            throw new AutoCompleterInitException("component is invalid");
        }
        this.textField = (JTextField) component;
        this.textField.addKeyListener(this);
        this.comboBoxModel = comboBox.getModel();
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // do nothing
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // do nothing
    }

    @Override
    public void keyReleased(KeyEvent e) {
        char keyChar = e.getKeyChar();
        if (Character.isAlphabetic(keyChar) || Character.isDigit(keyChar) || Character.isWhitespace(keyChar)
                || keyChar == KeyEvent.VK_BACK_SPACE) {
            String text = textField.getText();
            autoComplete(text);
            textField.setText(text);
        }
    }

    protected void autoComplete(String text) {
        E[] opts = getMatchingOptions(text);
        comboBox.setModel(new DefaultComboBoxModel<>(opts));
        if (comboBox.getItemCount() > 0) {
            comboBox.showPopup();
        }
    }

    @SuppressWarnings("unchecked")
    private E[] getMatchingOptions(String text) {
        List<E> list = new ArrayList<>();
        ComboBoxModel<E> tmp = this.comboBoxModel;
        for (int i = 0; i < tmp.getSize(); i++) {
            E itemObj = tmp.getElementAt(i);
            if (StringUtils.isEmpty(text) || StringUtils.containsIgnoreCase(itemObj.toString(), text)) {
                list.add(itemObj);
            }
        }
        return (E[]) list.toArray();
    }

}