From 81c1d53cdb86dadc19f77557ad99c20a58e52d66 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 8 Dec 2017 10:03:07 +0530 Subject: [PATCH] 8191639: NPE from BasicListUI.Actions.getNextPageIndex Reviewed-by: serb, ssadetsky --- .../javax/swing/plaf/basic/BasicListUI.java | 111 ++++++++++++---- test/jdk/javax/swing/JList/BasicListTest.java | 125 ++++++++++++++++++ 2 files changed, 212 insertions(+), 24 deletions(-) create mode 100644 test/jdk/javax/swing/JList/BasicListTest.java diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java index c175cd02f23..d4cf4a08027 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java @@ -2134,8 +2134,11 @@ public class BasicListUI extends ListUI Rectangle leadRect = (lead==-1) ? new Rectangle() : list.getCellBounds(lead, lead); + if (leadRect == null) { + return index; + } if (list.getLayoutOrientation() == JList.VERTICAL_WRAP && - list.getVisibleRowCount() <= 0) { + list.getVisibleRowCount() <= 0) { if (!list.getComponentOrientation().isLeftToRight()) { direction = -direction; } @@ -2146,14 +2149,20 @@ public class BasicListUI extends ListUI visRect.x = leadRect.x + leadRect.width - visRect.width; Point p = new Point(visRect.x - 1, leadRect.y); index = list.locationToIndex(p); + if (index == -1) { + return index; + } Rectangle cellBounds = list.getCellBounds(index, index); - if (visRect.intersects(cellBounds)) { + if (cellBounds != null && visRect.intersects(cellBounds)) { p.x = cellBounds.x - 1; index = list.locationToIndex(p); + if (index == -1) { + return index; + } cellBounds = list.getCellBounds(index, index); } // this is necessary for right-to-left orientation only - if (cellBounds.y != leadRect.y) { + if (cellBounds != null && cellBounds.y != leadRect.y) { p.x = cellBounds.x + cellBounds.width; index = list.locationToIndex(p); } @@ -2163,13 +2172,19 @@ public class BasicListUI extends ListUI visRect.x = leadRect.x; Point p = new Point(visRect.x + visRect.width, leadRect.y); index = list.locationToIndex(p); + if (index == -1) { + return index; + } Rectangle cellBounds = list.getCellBounds(index, index); - if (visRect.intersects(cellBounds)) { + if (cellBounds != null && visRect.intersects(cellBounds)) { p.x = cellBounds.x + cellBounds.width; index = list.locationToIndex(p); + if (index == -1) { + return index; + } cellBounds = list.getCellBounds(index, index); } - if (cellBounds.y != leadRect.y) { + if (cellBounds != null && cellBounds.y != leadRect.y) { p.x = cellBounds.x - 1; index = list.locationToIndex(p); } @@ -2187,17 +2202,23 @@ public class BasicListUI extends ListUI visRect.y = leadRect.y + leadRect.height - visRect.height; p.y = visRect.y; index = list.locationToIndex(p); + if (index == -1) { + return index; + } Rectangle cellBounds = list.getCellBounds(index, index); // go one cell down if first visible cell doesn't fit // into adjasted visible rectangle - if (cellBounds.y < visRect.y) { + if (cellBounds != null && cellBounds.y < visRect.y) { p.y = cellBounds.y + cellBounds.height; index = list.locationToIndex(p); + if (index == -1) { + return index; + } cellBounds = list.getCellBounds(index, index); } // if index isn't less then lead // try to go to cell previous to lead - if (cellBounds.y >= leadRect.y) { + if (cellBounds != null && cellBounds.y >= leadRect.y) { p.y = leadRect.y - 1; index = list.locationToIndex(p); } @@ -2207,15 +2228,23 @@ public class BasicListUI extends ListUI // down // go to the last completely visible cell Point p = new Point(leadRect.x, - visRect.y + visRect.height - 1); + visRect.y + visRect.height - 1); index = list.locationToIndex(p); + if (index == -1) { + return index; + } Rectangle cellBounds = list.getCellBounds(index, index); // go up one cell if last visible cell doesn't fit // into visible rectangle - if (cellBounds.y + cellBounds.height > - visRect.y + visRect.height) { + if (cellBounds != null && + cellBounds.y + cellBounds.height > + visRect.y + visRect.height) + { p.y = cellBounds.y - 1; index = list.locationToIndex(p); + if (index == -1) { + return index; + } cellBounds = list.getCellBounds(index, index); index = Math.max(index, lead); } @@ -2226,24 +2255,33 @@ public class BasicListUI extends ListUI visRect.y = leadRect.y; p.y = visRect.y + visRect.height - 1; index = list.locationToIndex(p); + if (index == -1) { + return index; + } cellBounds = list.getCellBounds(index, index); // go one cell up if last visible cell doesn't fit // into adjasted visible rectangle - if (cellBounds.y + cellBounds.height > - visRect.y + visRect.height) { + if (cellBounds != null && + cellBounds.y + cellBounds.height > + visRect.y + visRect.height) + { p.y = cellBounds.y - 1; index = list.locationToIndex(p); + if (index == -1) { + return index; + } cellBounds = list.getCellBounds(index, index); } // if index isn't greater then lead // try to go to cell next after lead - if (cellBounds.y <= leadRect.y) { + if (cellBounds != null && cellBounds.y <= leadRect.y) { p.y = leadRect.y + leadRect.height; index = list.locationToIndex(p); } } } } + return index; } @@ -2308,18 +2346,27 @@ public class BasicListUI extends ListUI cellBounds.x + cellBounds.width - visRect.width); int startIndex = list.locationToIndex(new Point(x, cellBounds.y)); + if (startIndex == -1) { + return; + } Rectangle startRect = list.getCellBounds(startIndex, startIndex); - if (startRect.x < x && startRect.x < cellBounds.x) { + if (startRect != null && + startRect.x < x && startRect.x < cellBounds.x) { startRect.x += startRect.width; startIndex = list.locationToIndex(startRect.getLocation()); + if (startIndex == -1) { + return; + } startRect = list.getCellBounds(startIndex, startIndex); } cellBounds = startRect; } - cellBounds.width = visRect.width; + if (cellBounds != null) { + cellBounds.width = visRect.width; + } } else { if (direction > 0) { @@ -2327,15 +2374,20 @@ public class BasicListUI extends ListUI int x = cellBounds.x + visRect.width; int rightIndex = list.locationToIndex(new Point(x, cellBounds.y)); + if (rightIndex == -1) { + return; + } Rectangle rightRect = list.getCellBounds(rightIndex, rightIndex); - if (rightRect.x + rightRect.width > x && - rightRect.x > cellBounds.x) { - rightRect.width = 0; + if (rightRect != null) { + if (rightRect.x + rightRect.width > x && + rightRect.x > cellBounds.x) { + rightRect.width = 0; + } + cellBounds.x = Math.max(0, + rightRect.x + rightRect.width - visRect.width); + cellBounds.width = visRect.width; } - cellBounds.x = Math.max(0, - rightRect.x + rightRect.width - visRect.width); - cellBounds.width = visRect.width; } else { cellBounds.x += Math.max(0, @@ -2357,24 +2409,35 @@ public class BasicListUI extends ListUI cellBounds.y + cellBounds.height - visRect.height); int startIndex = list.locationToIndex(new Point(cellBounds.x, y)); + if (startIndex == -1) { + return; + } Rectangle startRect = list.getCellBounds(startIndex, startIndex); - if (startRect.y < y && startRect.y < cellBounds.y) { + if (startRect != null && + startRect.y < y && startRect.y < cellBounds.y) { startRect.y += startRect.height; startIndex = list.locationToIndex(startRect.getLocation()); + if (startIndex == -1) { + return; + } startRect = list.getCellBounds(startIndex, startIndex); } cellBounds = startRect; - cellBounds.height = visRect.height; + if (cellBounds != null) { + cellBounds.height = visRect.height; + } } else { // adjust height to fit into visible rectangle cellBounds.height = Math.min(cellBounds.height, visRect.height); } } - list.scrollRectToVisible(cellBounds); + if (cellBounds != null) { + list.scrollRectToVisible(cellBounds); + } } } diff --git a/test/jdk/javax/swing/JList/BasicListTest.java b/test/jdk/javax/swing/JList/BasicListTest.java new file mode 100644 index 00000000000..7a30305fa9c --- /dev/null +++ b/test/jdk/javax/swing/JList/BasicListTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 8191639 + * @headful + * @summary Verifies no NPE is thrown wjen pageup/down is pressed in a JList + * @run main BasicListTest + */ + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +class MyList extends JList { + // I need this to be able to unselect when clicking outside list content + @Override + public int locationToIndex(final Point location) { + final int n = super.locationToIndex(location); + //return n; + final Rectangle q = getCellBounds(n, n); + return q != null && q.contains(location)?n:-1; + } +} + +public class BasicListTest { + private static void initComponents() { + f = new JFrame(); + jScrollPane1 = new JScrollPane(); + list1 = new MyList(); + + f.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + + list1.setModel(new javax.swing.AbstractListModel() { + String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" }; + @Override + public int getSize() { return strings.length; } + @Override + public Object getElementAt(int i) { return strings[i]; } + }); + jScrollPane1.setViewportView(list1); + + f.getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER); + + f.pack(); + f.setVisible(true); + p = list1.getLocationOnScreen(); + } + + private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException | + UnsupportedLookAndFeelException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static void main(String args[]) throws Exception { + for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { + try { + SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf)); + System.out.println("Test for LookAndFeel " + laf.getClassName()); + SwingUtilities.invokeAndWait(() -> { + initComponents(); + }); + System.out.println("Test passed for LookAndFeel " + laf.getClassName()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Robot robot = new Robot(); + robot.setAutoDelay(200); + robot.mouseMove(p.x, p.y); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_PAGE_DOWN); + robot.keyRelease(KeyEvent.VK_PAGE_DOWN); + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + } + SwingUtilities.invokeAndWait(() -> { + f.dispose(); + }); + } + } + + private static JScrollPane jScrollPane1; + private static MyList list1; + private static Point p; + private static JFrame f; + +}