JDK-6462008 : ArrayOutOfBoundsException raised when SHIFT-selecting items in a JList
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_2.5.1
  • CPU: x86
  • Submitted: 2006-08-21
  • Updated: 2011-03-08
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u1Fixed 7 b03Fixed
Description
FULL PRODUCT VERSION :
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.9-34.EL.rob #14 Tue May 2 11:20:13 CDT 2006 i686 i686 i386 GNU/Linux
SunOS 5.8 Generic_117350_23 sun4u sparc SUNW,UltraSPARC-IIi-Engine

A DESCRIPTION OF THE PROBLEM :
When selecting items from a JList, a problem occurs when using the SHIFT key while selecting items from the list. This problem causes an ArrayOutOfBoundsException to be raised during a DefaultListModel.getElementAt() call on the indices returned from JList.getSelectedIndices(). It appears that the DefaultSelectionModel is not being properly updated as to the state of the highlighted items (when the shift key is used to select items).

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the included source code to run a program that will setup the scenario to follow:

Step 1. Select "Item 0" from second llist.
Step 2. Click Add
Step 3. Select "Item 0" from first list.
Step 4. Click Remove
Step 5. Select "Item 1" from first list.
Step 6. Click Add
Step 7. SHIFT-select "Item 1" from first list.
Step 8. Click Remove.

At Step 8 the Exception is generated.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
"Item 1" from the first list to be removed and added to the end of the second list.
ACTUAL -
ArrayIndexOutOfBoundException was raised.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 0 >= 0
	at java.util.Vector.elementAt(Vector.java:432)
	at javax.swing.DefaultListModel.getElementAt(DefaultListModel.java:70)
	at Tester3$3.actionPerformed(Tester3.java:53)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1849)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2169)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:234)
	at java.awt.Component.processMouseEvent(Component.java:5488)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3126)
	at java.awt.Component.processEvent(Component.java:5253)
	at java.awt.Container.processEvent(Container.java:1966)
	at java.awt.Component.dispatchEventImpl(Component.java:3955)
	at java.awt.Container.dispatchEventImpl(Container.java:2024)
	at java.awt.Component.dispatchEvent(Component.java:3803)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4212)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3892)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3822)
	at java.awt.Container.dispatchEventImpl(Container.java:2010)
	at java.awt.Window.dispatchEventImpl(Window.java:1774)
	at java.awt.Component.dispatchEvent(Component.java:3803)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class Tester3 extends JFrame {

    private JList list1;
    private DefaultListModel listModel1;

    private JList list2;
    private DefaultListModel listModel2;

    private JButton removeButton;
    private JButton addButton;

    public Tester3() {
        super("JList Selection Bug Tester");
        listModel1 = new DefaultListModel();
        list1 = new JList(listModel1);
        list1.setVisibleRowCount(10);

        list1.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    removeButton.setEnabled(!list1.isSelectionEmpty());
                }
            }
        });

        listModel2 = new DefaultListModel();
        list2 = new JList(listModel2);
        list2.setVisibleRowCount(10);

        list2.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    addButton.setEnabled(!list2.isSelectionEmpty());
                }
            }
        });


        removeButton = new JButton(new AbstractAction("Remove") {
            public void actionPerformed(ActionEvent e) {
                int[] selectedIndices = list1.getSelectedIndices();
                for (int i = 0; i < selectedIndices.length; ++i) {
                    listModel2.addElement(listModel1.getElementAt(
                            selectedIndices[i] - i));
                    listModel1.remove(selectedIndices[i] - i);
                }
            }
        });
        removeButton.setEnabled(false);

        addButton = new JButton(new AbstractAction("Add") {
            public void actionPerformed(ActionEvent e) {
                int[] selectedIndices = list2.getSelectedIndices();
                for (int i = 0; i < selectedIndices.length; ++i) {
                    listModel1.addElement(listModel2.getElementAt(
                            selectedIndices[i] - i));
                    listModel2.remove(selectedIndices[i] - i);
                }
            }
        });
        addButton.setEnabled(false);

        JPanel bottomPanel = new JPanel();
        bottomPanel.add(removeButton);
        bottomPanel.add(addButton);
        JPanel centerPanel = new JPanel();
        centerPanel.add(new JScrollPane(list1));
        centerPanel.add(new JScrollPane(list2));
        add(centerPanel, BorderLayout.CENTER);
        add(bottomPanel, BorderLayout.PAGE_END);
    }

    public void setupItems() {
        for (int i = 0; i < 10; ++i) {
            listModel2.addElement("Item " + i);
        }
    }

    public static void main(String args[]) {
        Tester3 jf = new Tester3();
        jf.pack();
        jf.setVisible(true);
        jf.setupItems();
    }
}
---------- END SOURCE ----------

Comments
EVALUATION DefaultListSelectionModel, which doesn't know anything about the number of items in the list, sometimes allows the lead/anchor to get past the end of the selection. In this case, it happens when all items are removed. The lead and anchor end up at zero. Adding a new item at position zero moves the lead and anchor to one (still past the end). When a SHIFT-CLICK happens, the code then selected from the anchor (one) through item zero. This results in a bogus selection. JTable has already been changed to handle a lead/anchor past the end. Unfortunately, JList has not yet been. These changes will be made now.
23-08-2006