JList/ListDataListener Autocomplete functionality
I'm trying to make an auto complete application and am not entirely sure how I need to implement the calls from what the user types to the ListDataListener and the logic behind actually narrowing the list. I haven't done much with JList's. The program has provided a list of locations that the JList is built off of.
/**
* Class to set up the GUI for our auto complete application
*/
public class AutoCompleteApplication extends JFrame
{
/**
* Not used
*/
private static final long serialVersionUID = 0;
/**
* JPanel to act as our container panel
*/
JPanel canvasPanel;
public AutoCompleteApplication()
{
super("Auto Complete Application");
/**
* Set up the file menu
*/
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic('F'); // set the mnemonic for the file menu
/**
* Set up exit menu item
*/
JMenuItem exitItem = new JMenuItem("Exit");
exitItem.setMnemonic('x'); // set the mnemonic for the exit menu item
fileMenu.add(exitItem); // add the exitItem to the file menu
/**
* Add the action listener for the exitItem
*/
exitItem.addActionListener(
// Anonymous inner class
new ActionListener()
{
// terminate application when user clicks exitItem
public void actionPerformed(ActionEvent event)
{
System.exit(0);
} // end method actionPerformed
}); // end call to addActionListener
/**
* Set up menu bar
*/
JMenuBar menuBar = new JMenuBar(); // create the menu bar
setJMenuBar(menuBar); // add menu bar to application
menuBar.add(fileMenu); // add file menu to the menu bar
/**
* Instantiate canvas panel to add to the layout
*/
canvasPanel = new JPanel();
/**
* Set up layout
*/
canvasPanel.setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 0.0;
constraints.weighty = 1.0;
constraints.gridheight = 1;
constraints.fill = GridBagConstraints.BOTH;
/**
* Add components to panel
*/
canvasPanel.add(new AutoCompletePanel(), constraints);
/*constraints.gridx = 0;
constraints.gridy = 0;
add(new SearchFieldPanel(), constraints);
constraints.gridx = 0;
constraints.gridy = 1;
add(new ResultsPanel(), constraints);
*/
setDefaultCloseOperation(EXIT_ON_CLOSE);
/**
* Window listener to ensure DB connection is closed
*/
addWindowListener(
new WindowAdapter()
{
// disconnect from database and exit when window has closed
@Override
public void windowClosed(WindowEvent event)
{
System.exit(0);
} // end method windowClosed
} // end WindowAdapter inner class
); // end call to addWindowListener
} // end AutoCompleteApplication constructor
public static void main(String args[])
{
new AutoCompleteApplication().run();
} // end main
private void run()
{
add(canvasPanel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
} // end method run
} // end class AutoCompleteApplication
[/CODE]
The sub panel handling most of the component functionality at this point:
Code:
package net.hrtmn.a9;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.Document;
import library.Similarity;
public class AutoCompletePanel extends JPanel
{
/**
* JTextField for our auto-complete search field
*/
private final JTextField searchField;
/**
* JList for our list of locations
*/
private JList locationList;
/**
* LocationListModel for our locations JList
*/
private LocationsListModel locationsListModel;
/**
* ArrayList of Components. Components removed from JList are added
*/
private ArrayList<Component> components;
public AutoCompletePanel()
{
searchField = new JTextField(25);
// get the document for the JTextField
Document document = searchField.getDocument();
document.addDocumentListener(new MyDocumentListener());
// build our JList with values from file
locationsListModel = new LocationsListModel();
locationList = new JList(locationsListModel);
// add ListSelectionListener to the locationList
locationList.addListSelectionListener(new MyListSelectionListener());
// add the JList to a JScrollPane
JScrollPane scrollPane = new JScrollPane(locationList);
/**
* Set up layout
*/
setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 0.0;
constraints.weighty = 1.0;
constraints.gridheight = 1;
constraints.fill = GridBagConstraints.BOTH;
/**
* Add components to panel
*/
constraints.gridx = 0;
constraints.gridy = 0;
add(searchField, constraints);
constraints.gridx = 0;
constraints.gridy = 1;
add(scrollPane, constraints);
} // end constructor for AutoCompletePanel
/**
* Private inner class to handle Document Events
*/
private class MyDocumentListener implements DocumentListener
{
public void changedUpdate(DocumentEvent event)
{
//System.out.println(searchField.getText());
searchList();
} // end method changedUpdate
public void removeUpdate(DocumentEvent event)
{
//System.out.println(searchField.getText());
searchList();
} // end method removeUpdate
public void insertUpdate(DocumentEvent event)
{
//System.out.println(searchField.getText());
searchList();
} // end method insertUpdate
public void searchList()
{
for(int i = 0; i <= locationsListModel.size(); i++)
{
float similarity = Similarity.a_in_b(searchField.getText(), locationList.getComponent(i).toString());
if(similarity >= 0.6)
{
components.add(locationList.getComponent(i));
} // end if
}
for(int i = 0; i <= components.size(); i++)
{
System.out.println(components.get(i).toString());
}
} // end method searchList
} // end inner class MyDocumentListener
/**
* Private inner class for ListListener
*/
private class MyListSelectionListener implements ListSelectionListener
{
public void valueChanged(ListSelectionEvent event)
{
if (!event.getValueIsAdjusting()) {
// Set the value of the searchField to what the user has selected
searchField.setText(locationList.getSelectedValue().toString());
} // end if
} // end method valueChanged
} // end inner class MyListListener
} // end class AutoCompletePanel
Custom ListModel:
Code:
package net.hrtmn.a9;
import javax.swing.DefaultListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import library.InputList;
public class LocationsListModel extends DefaultListModel
{
private String[] data;
public LocationsListModel()
{
// instantiate InputList object and pass it a filename
InputList inputList = new InputList("PostalCodes.csv");
// get the values from file and assign them to a String array
data = inputList.get();
this.addListDataListener(new MyListDataListener());
} // end construct for LocationsListModel
@Override
public Object getElementAt(int index)
{
return data[index];
} // end method getElementAt
@Override
public int getSize()
{
return data.length;
} // end method getSize
@Override
public void addListDataListener(ListDataListener l)
{
} // end method addListDataListener
@Override
public void removeListDataListener(ListDataListener l)
{
} // end method removeListDataListener
public void listChange(ListDataListener listener)
{
} // end method listChange
private class MyListDataListener implements ListDataListener
{
public void contentsChanged(ListDataEvent listDataEvent)
{
}
public void intervalAdded(ListDataEvent listDataEvent)
{
}
public void intervalRemoved(ListDataEvent listDataEvent)
{
}
} // end inner class MyListDataListener
} // end class LocationsListModel
Supplied code for reading the file:
Code:
package library;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* Get an array of Strings representing lines from a given input resource.
*/
public class InputList
{
/**
* The name of the file resource.
*/
String filename;
/**
* Input a list of lines from the given file resource.
*
* @param filename the name of the file resource
*/
public InputList(String filename)
{
if (filename == null) {
throw new InvalidParameterException("File name is null");
}
this.filename = filename;
}
/**
* Read the file and return an array containing the lines.
*/
public String[] get()
{
InputStream is = getClass().getResourceAsStream(filename);
Scanner sc = new Scanner(is);
List<String> lines = new ArrayList<String>();
while (sc.hasNext()) {
lines.add(sc.nextLine());
}
try {
is.close();
}
catch (IOException iOException) {
}
return lines.toArray(new String[1]);
}
}
Supplied code to check for similarity between strings:
Code:
package library;
/**
* This class provides a method for computing the similarity between two strings.
* <p/>
* © 2010 Paladin Software International, Incorporated. All Rights Reserved.
*
* @version 1.0
*/
public final class Similarity
{
/**
* Maximum prefix length to use in adjusting Jaro style score to Jaro-Winkler score.
*/
private static final int PREFIXCOUNT = 6;
/**
* Weighting for the adjustment of the Jaro score.
*/
private static final float WEIGHT = 0.1F;
/**
* Maximum distance to look for possible transpositions of characters.
*/
private static final int MAXSEPARATION = 3;
/**
* Ignore case differences in comparisons.
*/
private static boolean ignoreCase = true;
/**
* Ignore punctuation in comparisons.
*/
private static boolean ignorePunctuation;
/**
* Prevent the class from being instantiated.
*/
private Similarity()
{
}
/**
* Gets a similarity measure of a String a within a String by using a modified form of the the Jaro-Winkler metric for
* the given strings. The properties <code>ignoreCase</code> and <code>ingnorePunctuation</code> control how and which
* characters are compared.
*
* @param a the trial String
* @param b the String to which it is to be compared.
*
* @return a score in the range 0.0-1.0
*/
public static float a_in_b(final String a, final String b)
{
/* Copy strings to StringBuilders, removing case differences and
punctuation if desired. */
StringBuilder asb = specialCopy(a);
StringBuilder bsb = specialCopy(b);
/* Quick test for identity. */
if (asb.equals(bsb)) {
return 1.0F;
}
/* Get common characters looking at it both ways. */
final StringBuilder common1 = find_common(asb, bsb);
final StringBuilder common2 = find_common(bsb, asb);
/* There is no similarity if no characters in common. */
if (common1.length() == 0 && common2.length() == 0) {
return 0.0F;
}
/* Check for same length of common strings; return zero if not the
same. */
if (common1.length() != common2.length()
|| asb.length() > bsb.length()) {
return 0.0F;
}
/* Get the number of transpositions. */
int transpositions = 0;
for (int i = 0; i < common1.length(); i++) {
if (common1.charAt(i) != common2.charAt(i)) {
transpositions++;
}
}
transpositions /= 2.0F;
/* Calculate the Jaro metric. */
final float dist =
(common1.length() / ((float) asb.length())
+ common2.length() / ((float) bsb.length())
+ (common1.length() - transpositions)
/ ((float) common1.length())) / 3.0F;
/* This extension modifies the weights of poorly matching pairs a, b which
share a common prefix gets the prefix length found of common characters
at the begining of the strings. */
int len = Math.min(PREFIXCOUNT, Math.min(asb.length(), bsb.length()));
/* Check for prefix similarity of length len. */
for (int i = 0; i != len; i++) /* Check the prefix is the same so far. */ {
if (asb.charAt(i) != b.charAt(i)) {
/* Not the same, so return as far as have gotten. */
len = i;
break;
}
}
return dist + ((float) len * WEIGHT * (1.0F - dist));
}
/**
* Set the value of the property saying to ignore case differences. Default value is <code>true</code).
*
* @param ignoreCase the desired value of the property
*/
public static void setIgnoreCase(boolean ignoreCase)
{
Similarity.ignoreCase = ignoreCase;
}
/**
* Get the the value of property saying to ignore case differences. Default value is <code>true</code).
*/
public static boolean getIgnoreCase()
{
return Similarity.ignoreCase;
}
/**
* Set the value of the property saying to ignore punctuation. Default value is <code>false</code).
*
* @param ignorePunctuation the desired value of the property
*/
public static void setIgnorePunctuation(boolean ignorePunctuation)
{
Similarity.ignorePunctuation = ignorePunctuation;
}
/**
* Get the the value of property saying to ignore punctuation. Default value is <code>false</code).
*/
public static boolean getIgnorePunctuation()
{
return Similarity.ignorePunctuation;
}
/**
* Returns a string buffer of characters from sb1 that also occur in sb2 if they are within a given distance separation
* from the position in sb1.
*/
private static StringBuilder find_common(StringBuilder sb1, StringBuilder sb2)
{
/* Create a buffer of characters for the return value*/
StringBuilder result = new StringBuilder();
/* Get half the length of the longest string, rounded up - (this is the
distance used for acceptable transpositions, but limited by the
MAXSEPARATION value). */
int separation = Math.min(MAXSEPARATION,
Math.max((sb1.length() + 1) / 2, (sb2.length() + 1) / 2));
/* Iterate over sb1, looking at each character in sb2. */
for (int i = 0; i != sb1.length(); ++i) {
char ch = sb1.charAt(i);
/* Look either way in sb2 from the current position in sb1, plus or minus the
separation distance, for the character from sb1. */
int from = Math.max(0, i - separation);
if (from == sb2.length()) {
break;
}
int to = Math.min(i + separation, sb2.length() - 1) + 1;
for (int j = from; j != to; ++j) /* If there is a matching character, then add it to the common
character list. */ {
if (sb2.charAt(j) == ch) {
int k;
/* If the character is already in the common set, give up. */
for (k = 0; k != result.length(); ++k) {
if (result.charAt(k) == ch) {
break;
}
}
/* If loop exhausted, the character is not there, so place
it there, and then give up on looking any further. */
if (k == result.length()) {
result.append(ch);
break;
}
}
}
}
return result;
}
/**
* Returns a StringBuilder copy of the input CharSequence with punctuation characters removed.
*
* @param input the input character sequence
*/
private static StringBuilder specialCopy(CharSequence input)
{
StringBuilder output = new StringBuilder();
for (int i = 0; i != input.length(); ++i) {
char c = ignoreCase
? Character.toLowerCase(input.charAt(i)) : input.charAt(i);
if (ignorePunctuation) {
int type = Character.getType(c);
if (type == Character.CONNECTOR_PUNCTUATION
|| type == Character.DASH_PUNCTUATION
|| type == Character.END_PUNCTUATION
|| type == Character.FINAL_QUOTE_PUNCTUATION
|| type == Character.INITIAL_QUOTE_PUNCTUATION
|| type == Character.OTHER_PUNCTUATION
|| type == Character.START_PUNCTUATION) {
continue;
}
}
output.append(c);
}
return output;
}
}
If anyone can help it would be greatly appreciated!