Saturday, March 14, 2009

Bug in GlazedLists' AutoCompleteSupport?

I found what looks to me like a couple bugs in the AutoCompleteSupport of GlazedLists. I'm not going to talk about how cool GlazedLists is - it's very cool. I just want to document the bugs. Either that, or document that I'm a bonehead and I'm doing something bass-ackwards with a perfectly good library.

Note: I am doing something boneheaded. I'm documenting a known bug (bug 458). I spammed GlazedLists' mailing list. The first issue, selecting items from the popup, I reported as bug 469.

Here's my repro. I'm working on OS X with Java 10.5.0_16, which I suspect is a factor.

package org.cbare.testglazedlists;

import javax.swing.*;

import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.swing.AutoCompleteSupport;


public class SpeciesChooser {
 JFrame frame;
 AutoCompleteSupport autocomplete;
 private JComboBox chooser;

 public SpeciesChooser() {
  initGui();
 }

 private void initGui() {
  Box vbox, hbox;
  frame = new JFrame("Species Chooser");
  
  vbox = Box.createVerticalBox();
  vbox.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
  hbox = Box.createHorizontalBox();
  
  hbox.add(new JLabel("Select Species:"));
  chooser = new JComboBox();
  chooser.setPrototypeDisplayValue("Marmoset");
  hbox.add(chooser);
  SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    autocomplete = AutoCompleteSupport.install(chooser, getSpecies());
   }
  });

  vbox.add(hbox);
  frame.add(vbox);
  frame.pack();
  frame.setVisible(true);
 }

 private EventList getSpecies() {
  EventList result = new BasicEventList();
  result.add("Marmoset");
  result.add("Monkey");
  result.add("Moose");
  result.add("Mouse");
  result.add("Spaaa");
  result.add("Spider");
  result.add("Spidooo");
  return result;
 }

 public static void main(String[] args) {
  SpeciesChooser s = new SpeciesChooser();
 }
}

I used Jing to make a little screencast of my flailing about. Instructions for the repro follow:

First, if I type a prefix which matches several item in the list, and I use the down-arrow to scroll through the matches and pick one, it fails to update the context of the text box. In my example, I type "mo" which matches, Monkey, Moose, Mouse. I press down-arrow twice to select Moose then press Enter. "Monkey" is still in the text box. If I repeat that a couple of times, the popup then shows all choices, not just those matching my prefix. Maybe that's intentional.

Second, if I type something that matches nothing in the list, say "moz" and then backspace over the "z" I get an exception. The stack trace is shown here:

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
    at java.util.ArrayList.RangeCheck(ArrayList.java:546)
    at java.util.ArrayList.get(ArrayList.java:321)
    at ca.odell.glazedlists.impl.gui.ThreadProxyEventList.applyChangeToCache(ThreadProxyEventList.java:175)
    at ca.odell.glazedlists.impl.gui.ThreadProxyEventList.access$600(ThreadProxyEventList.java:68)
    at ca.odell.glazedlists.impl.gui.ThreadProxyEventList$UpdateRunner.listChanged(ThreadProxyEventList.java:237)
    at ca.odell.glazedlists.event.ListEventAssembler$ListEventFormat.fire(ListEventAssembler.java:412)
    at ca.odell.glazedlists.event.ListEventAssembler$ListEventFormat.fire(ListEventAssembler.java:409)
    at ca.odell.glazedlists.event.SequenceDependenciesEventPublisher$SubjectAndListener.firePendingEvent(SequenceDependenciesEventPublisher.java:445)
    at ca.odell.glazedlists.event.SequenceDependenciesEventPublisher.fireEvent(SequenceDependenciesEventPublisher.java:344)
    at ca.odell.glazedlists.event.ListEventAssembler.commitEvent(ListEventAssembler.java:316)
    at ca.odell.glazedlists.impl.gui.ThreadProxyEventList$UpdateRunner.run(ThreadProxyEventList.java:225)
    at ca.odell.glazedlists.impl.swing.SwingThreadProxyEventList.schedule(SwingThreadProxyEventList.java:33)
    at ca.odell.glazedlists.impl.gui.ThreadProxyEventList.listChanged(ThreadProxyEventList.java:118)
    at ca.odell.glazedlists.event.ListEventAssembler$ListEventFormat.fire(ListEventAssembler.java:412)
    at ca.odell.glazedlists.event.ListEventAssembler$ListEventFormat.fire(ListEventAssembler.java:409)
    at ca.odell.glazedlists.event.SequenceDependenciesEventPublisher$SubjectAndListener.firePendingEvent(SequenceDependenciesEventPublisher.java:445)
    at ca.odell.glazedlists.event.SequenceDependenciesEventPublisher.fireEvent(SequenceDependenciesEventPublisher.java:344)
    at ca.odell.glazedlists.event.ListEventAssembler.commitEvent(ListEventAssembler.java:316)
    at ca.odell.glazedlists.FilterList.constrained(FilterList.java:389)
    at ca.odell.glazedlists.FilterList.changeMatcher(FilterList.java:286)
    at ca.odell.glazedlists.FilterList.changeMatcherWithLocks(FilterList.java:269)
    at ca.odell.glazedlists.FilterList.access$100(FilterList.java:51)
    at ca.odell.glazedlists.FilterList$PrivateMatcherEditorListener.changedMatcher(FilterList.java:443)
    at ca.odell.glazedlists.matchers.AbstractMatcherEditor.fireChangedMatcher(AbstractMatcherEditor.java:115)
    at ca.odell.glazedlists.matchers.AbstractMatcherEditor.fireConstrained(AbstractMatcherEditor.java:73)
    at ca.odell.glazedlists.matchers.TextMatcherEditor.setTextMatcher(TextMatcherEditor.java:321)
    at ca.odell.glazedlists.matchers.TextMatcherEditor.setFilterText(TextMatcherEditor.java:292)
    at ca.odell.glazedlists.swing.AutoCompleteSupport.applyFilter(AutoCompleteSupport.java:1271)
    at ca.odell.glazedlists.swing.AutoCompleteSupport.access$2300(AutoCompleteSupport.java:209)
    at ca.odell.glazedlists.swing.AutoCompleteSupport$AutoCompleteFilter.postProcessDocumentChange(AutoCompleteSupport.java:1497)
    at ca.odell.glazedlists.swing.AutoCompleteSupport$AutoCompleteFilter.remove(AutoCompleteSupport.java:1450)
    at javax.swing.text.AbstractDocument.remove(AbstractDocument.java:572)
    at javax.swing.text.DefaultEditorKit$DeletePrevCharAction.actionPerformed(DefaultEditorKit.java:1030)
    at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1576)
    at javax.swing.JComponent.processKeyBinding(JComponent.java:2772)
    ...

In spite of my whining, GlazedLists is very well thought out and a nice piece of work. For later reference, I also reported bug 472.