JDK-6794836 : BasicSliderUI throws NullPointerExc when JSlider maximum is Integer.MAX_VALUE
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2009-01-16
  • Updated: 2011-01-19
  • Resolved: 2009-02-13
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
6u14 b02Fixed 7Fixed
Description
FULL PRODUCT VERSION :
Java(TM) SE Runtime Environment (build 1.6.0_07-b06)
Java HotSpot(TM) Client VM (build 10.0-b23, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Linux artemis 2.6.24-21-generic #1 SMP Mon Aug 25 17:32:09 UTC 2008 i686 GNU/Linux (Kubuntu Hardy)
Windows XP SP 3


A DESCRIPTION OF THE PROBLEM :
We have existing GUI code that uses a JSlider. It uses values between 0 and Integer.MAX_VALUE. It works fine on machines that run Java 5 (1.5.0_16), but it throws a NullPointerException (see below) when using Java 6.

The reason is that BasicSliderUI was changed in Java 6. getHighestValueLabel() now calls the new method getLowestValue(), which is buggy. Let's take a look:

    protected Integer getLowestValue() {
        Dictionary dictionary = slider.getLabelTable();
        if (dictionary != null) {
            Enumeration keys = dictionary.keys();
            int min = slider.getMaximum() + 1;
            while (keys.hasMoreElements()) {
                min = Math.min(min, ((Integer)keys.nextElement()).intValue());
            }
            if (min == slider.getMaximum() + 1) {
                return null;
            }
            return min;
        }
        return null;
    }

If one of the keys is Integer.MAX_VALUE, then min is initialized with Integer.MAX_VALUE + 1 = Integer.MIN_VALUE (integer overflow).
The loop doesn't change anything because Math.min(Integer.MIN_VALUE, x) always returns Integer.MIN_VALUE.
So min == slider.getMaximum() + 1, and this method returns null.

I have rewritten this method for my workaround. When using Java 6, my method will override the buggy method. When using Java 5, my workaround won't have any effects (which is good).

Please review my code and consider to include it in BasicSlideUI.java.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the attached source code.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Slider with labels at zero and maximum.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.NullPointerException
        at javax.swing.plaf.basic.BasicSliderUI.calculateTrackBuffer(BasicSliderUI.java:585)
        at javax.swing.plaf.basic.BasicSliderUI.calculateGeometry(BasicSliderUI.java:510)
        at javax.swing.plaf.basic.BasicSliderUI$Handler.propertyChange(BasicSliderUI.java:1448)
        at javax.swing.plaf.basic.BasicSliderUI$PropertyChangeHandler.propertyChange(BasicSliderUI.java:725)
        at javax.swing.plaf.metal.MetalSliderUI$MetalPropertyListener.propertyChange(MetalSliderUI.java:129)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276)
        at java.awt.Component.firePropertyChange(Component.java:7868)
        at javax.swing.JSlider.setLabelTable(JSlider.java:794)
        at SliderTest.main(SliderTest.java:41)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.plaf.basic.BasicSliderUI;

public class SliderTest {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Test");

        JSlider slider = new JSlider(0, Integer.MAX_VALUE);

        slider.setPaintLabels(true);

        Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
        labelTable.put(0, new JLabel("Zero"));
        labelTable.put(Integer.MAX_VALUE, new JLabel("Maximum"));

        slider.setLabelTable(labelTable);
        frame.add(slider);

        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.plaf.basic.BasicSliderUI;

public class SliderTest {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Test");

        JSlider slider = new JSlider(0, Integer.MAX_VALUE);

// ######### START WORKAROUND ################
//        slider.setUI(new BasicSliderUI(slider) {
//            protected Integer getLowestValue() {
//                Dictionary dictionary = slider.getLabelTable();
//                if (dictionary != null) {
//                    Enumeration keys = dictionary.keys();
//                    if (!keys.hasMoreElements()) {
//                        return null;
//                    }
//
//                    int min = Integer.MAX_VALUE;
//                    while (keys.hasMoreElements()) {
//                        min = Math.min(min, ((Integer) keys.nextElement())
//                                .intValue());
//                    }
//                    return min;
//                }
//                return null;
//            }
//        });
// ######### END WORKAROUND ################

        slider.setPaintLabels(true);

        Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
        labelTable.put(0, new JLabel("Zero"));
        labelTable.put(Integer.MAX_VALUE, new JLabel("Maximum"));

        slider.setLabelTable(labelTable);
        frame.add(slider);

        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Release Regression From : 6
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
EVALUATION We should take into account such values as MAX_INT
21-01-2009