From 333a55103ed3e480793539f7329bff6e23771047 Mon Sep 17 00:00:00 2001 From: Antonio Vieiro Date: Fri, 26 Sep 2025 00:10:57 +0000 Subject: [PATCH 1/6] 8348760: RadioButton is not shown if JRadioButtonMenuItem is rendered with ImageIcon in WindowsLookAndFeel 8365375: Method SU3.setAcceleratorSelectionForeground assigns to acceleratorForeground Reviewed-by: sgehwolf, andrew Backport-of: 31fc05c4721aa1aaf12e5f326a52c1db48006abb --- .../com/sun/java/swing/SwingUtilities3.java | 137 ++++++++++- .../windows/WindowsCheckBoxMenuItemUI.java | 20 +- .../plaf/windows/WindowsIconFactory.java | 16 +- .../swing/plaf/windows/WindowsMenuItemUI.java | 140 ++++++++++- .../swing/plaf/windows/WindowsMenuUI.java | 21 +- .../windows/WindowsRadioButtonMenuItemUI.java | 19 +- .../swing/plaf/basic/BasicMenuItemUI.java | 144 ++++------- .../TestRadioAndCheckMenuItemWithIcon.java | 228 ++++++++++++++++++ 8 files changed, 618 insertions(+), 107 deletions(-) create mode 100644 jdk/test/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java diff --git a/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java b/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java index b50fb2ae1b..c29297a630 100644 --- a/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, 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 @@ -36,11 +36,20 @@ import java.util.concurrent.Callable; import java.applet.Applet; import java.awt.AWTEvent; import java.awt.EventQueue; +import java.awt.Color; import java.awt.Component; import java.awt.Container; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; import java.awt.Window; +import javax.swing.ButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; +import javax.swing.JMenu; import javax.swing.RepaintManager; +import sun.swing.MenuItemLayoutHelper; +import sun.swing.SwingUtilities2; /** * A collection of utility methods for Swing. @@ -61,6 +70,10 @@ public class SwingUtilities3 { private static final Object DELEGATE_REPAINT_MANAGER_KEY = new StringBuilder("DelegateRepaintManagerKey"); + private static Color disabledForeground; + private static Color acceleratorSelectionForeground; + private static Color acceleratorForeground; + /** * Registers delegate RepaintManager for {@code JComponent}. */ @@ -113,6 +126,128 @@ public class SwingUtilities3 { return Boolean.TRUE == vsyncedMap.get(rootContainer); } + public static void applyInsets(Rectangle rect, Insets insets) { + if (insets != null) { + rect.x += insets.left; + rect.y += insets.top; + rect.width -= (insets.right + rect.x); + rect.height -= (insets.bottom + rect.y); + } + } + + public static void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color holdc, Color foreground) { + if (lh.getCheckIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } else { + g.setColor(holdc); + } + if (lh.useCheckAndArrow()) { + lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, + lr.getCheckRect().x, lr.getCheckRect().y); + } + g.setColor(holdc); + } + } + + public static void paintIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc) { + if (lh.getIcon() != null) { + Icon icon; + ButtonModel model = lh.getMenuItem().getModel(); + if (!model.isEnabled()) { + icon = lh.getMenuItem().getDisabledIcon(); + } else if (model.isPressed() && model.isArmed()) { + icon = lh.getMenuItem().getPressedIcon(); + if (icon == null) { + // Use default icon + icon = lh.getMenuItem().getIcon(); + } + } else { + icon = lh.getMenuItem().getIcon(); + } + + if (icon != null) { + icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, + lr.getIconRect().y); + g.setColor(holdc); + } + } + } + + + public static void paintAccText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (!lh.getAccText().isEmpty()) { + ButtonModel model = lh.getMenuItem().getModel(); + g.setFont(lh.getAccFontMetrics().getFont()); + if (!model.isEnabled()) { + + // paint the accText disabled + if (disabledForeground != null) { + g.setColor(disabledForeground); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + } else { + g.setColor(lh.getMenuItem().getBackground().brighter()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + g.setColor(lh.getMenuItem().getBackground().darker()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x - 1, + lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); + } + } else { + + // paint the accText normally + if (model.isArmed() + || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(acceleratorSelectionForeground); + } else { + g.setColor(acceleratorForeground); + } + SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), + lr.getAccRect().x, lr.getAccRect().y + + lh.getAccFontMetrics().getAscent()); + } + } + } + + public static void setDisabledForeground(Color disabledFg) { + disabledForeground = disabledFg; + } + + public static void setAcceleratorSelectionForeground(Color acceleratorSelectionFg) { + acceleratorSelectionForeground = acceleratorSelectionFg; + } + + public static void setAcceleratorForeground(Color acceleratorFg) { + acceleratorForeground = acceleratorFg; + } + + public static void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color foreground) { + if (lh.getArrowIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } + if (lh.useCheckAndArrow()) { + lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, + lr.getArrowRect().x, lr.getArrowRect().y); + } + } + } + /** * Returns delegate {@code RepaintManager} for {@code component} hierarchy. */ diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index ab1a1bce0b..fdf109c9dd 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -74,6 +74,24 @@ public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { } super.paintBackground(g, menuItem, bgColor); } + + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, defaultTextIconGap, + menuItem, getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + /** * Method which renders the text of the current menu item. *

diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 90bdc92938..7fda45b254 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, 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 @@ -785,6 +785,7 @@ public class WindowsIconFactory implements Serializable } assert menuItem == null || c == menuItem; Icon icon = getIcon(); + if (type == JCheckBoxMenuItem.class || type == JRadioButtonMenuItem.class) { AbstractButton b = (AbstractButton) c; @@ -808,19 +809,18 @@ public class WindowsIconFactory implements Serializable } XPStyle xp = XPStyle.getXP(); if (xp != null) { - Skin skin; - skin = xp.getSkin(c, backgroundPart); - skin.paintSkin(g, x, y, - getIconWidth(), getIconHeight(), backgroundState); - if (icon == null) { - skin = xp.getSkin(c, part); + Skin skin = xp.getSkin(c, part); + if (icon == null || icon.getIconHeight() <= 16) { skin.paintSkin(g, x + OFFSET, y + OFFSET, state); + } else { + skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state); } } } } if (icon != null) { - icon.paintIcon(c, g, x + OFFSET, y + OFFSET); + icon.paintIcon(c, g, x + VistaMenuItemCheckIconFactory.getIconWidth(), + y + OFFSET); } } private static WindowsMenuItemUIAccessor getAccessor( diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index dddbeeb507..41995f322d 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -31,9 +31,11 @@ import javax.swing.plaf.*; import javax.swing.plaf.basic.*; import sun.swing.SwingUtilities2; +import sun.swing.MenuItemLayoutHelper; import com.sun.java.swing.plaf.windows.TMSchema.*; import com.sun.java.swing.plaf.windows.XPStyle.*; +import com.sun.java.swing.SwingUtilities3; /** * Windows rendition of the component. @@ -47,8 +49,12 @@ import com.sun.java.swing.plaf.windows.XPStyle.*; * * @author Igor Kushnirskiy */ - public class WindowsMenuItemUI extends BasicMenuItemUI { + + private static Color disabledForeground; + private static Color acceleratorSelectionForeground; + private static Color acceleratorForeground; + final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { @@ -68,6 +74,136 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { return new WindowsMenuItemUI(); } + protected void installDefaults() { + super.installDefaults(); + String prefix = getPropertyPrefix(); + + if (acceleratorSelectionForeground == null || + acceleratorSelectionForeground instanceof UIResource) { + acceleratorSelectionForeground = + UIManager.getColor(prefix + ".acceleratorSelectionForeground"); + } + if (acceleratorForeground == null || + acceleratorForeground instanceof UIResource) { + acceleratorForeground = + UIManager.getColor(prefix + ".acceleratorForeground"); + } + if (disabledForeground == null || + disabledForeground instanceof UIResource) { + disabledForeground = + UIManager.getColor(prefix + ".disabledForeground"); + } + } + + + private static void applyInsets(Rectangle rect, Insets insets) { + SwingUtilities3.applyInsets(rect, insets); + } + + private static void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color holdc, Color foreground) { + SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); + } + + private static void paintIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc) { + SwingUtilities3.paintIcon(g, lh, lr, holdc); + } + + private static void paintAccText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + SwingUtilities3.setDisabledForeground(disabledForeground); + SwingUtilities3.setAcceleratorSelectionForeground( + acceleratorSelectionForeground); + SwingUtilities3.setAcceleratorForeground(acceleratorForeground); + SwingUtilities3.paintAccText(g, lh, lr); + } + + private static void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color foreground) { + SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); + } + + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, + defaultTextIconGap, menuItem, + getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + + static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g, + JComponent c, Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap, JMenuItem menuItem, String prefix) { + // Save original graphics font and color + Font holdf = g.getFont(); + Color holdc = g.getColor(); + + JMenuItem mi = (JMenuItem) c; + g.setFont(mi.getFont()); + + Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); + applyInsets(viewRect, mi.getInsets()); + + String acceleratorDelimiter = + UIManager.getString("MenuItem.acceleratorDelimiter"); + if (acceleratorDelimiter == null) { acceleratorDelimiter = "+"; } + Font acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont"); + if (acceleratorFont == null) { + acceleratorFont = UIManager.getFont("MenuItem.font"); + } + + MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, + arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter, + mi.getComponentOrientation().isLeftToRight(), mi.getFont(), + acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem), + prefix); + MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); + + paintBackground(accessor, g, mi, background); + paintCheckIcon(g, lh, lr, holdc, foreground); + paintIcon(g, lh, lr, holdc); + + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getTextRect(); + + rect.x += lh.getAfterCheckIconGap(); + + lr.setTextRect(rect); + } + if (!lh.getText().isEmpty()) { + if (lh.getHtmlView() != null) { + // Text is HTML + lh.getHtmlView().paint(g, lr.getTextRect()); + } else { + // Text isn't HTML + paintText(accessor, g, lh.getMenuItem(), + lr.getTextRect(), lh.getText()); + } + } + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getAccRect(); + rect.x += lh.getAfterCheckIconGap(); + lr.setAccRect(rect); + } + paintAccText(g, lh, lr); + paintArrowIcon(g, lh, lr, foreground); + + // Restore original graphics font and color + g.setColor(holdc); + g.setFont(holdf); + } + /** * Method which renders the text of the current menu item. *

diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index a2e7795cfe..d81e172a28 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -123,6 +123,25 @@ public class WindowsMenuUI extends BasicMenuUI { hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true; } + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, arrowIcon, + background, foreground, + defaultTextIconGap, menuItem, + getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + + /** * Draws the background of the menu. * @since 1.4 diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index c43876f38b..5500791603 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -74,6 +74,23 @@ public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { super.paintBackground(g, menuItem, bgColor); } + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, defaultTextIconGap, + menuItem, getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + /** * Method which renders the text of the current menu item. *

diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java index 54994b61e3..0d45bb54f0 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -25,17 +25,52 @@ package javax.swing.plaf.basic; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.InputMap; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.MenuDragMouseEvent; +import javax.swing.event.MenuDragMouseListener; +import javax.swing.event.MenuKeyListener; + +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ComponentInputMapUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.MenuItemUI; +import javax.swing.plaf.UIResource; import javax.swing.text.View; -import sun.swing.*; +import com.sun.java.swing.SwingUtilities3; +import sun.swing.MenuItemCheckIconFactory; +import sun.swing.MenuItemLayoutHelper; +import sun.swing.SwingUtilities2; +import sun.swing.UIAction; + /** * BasicMenuItem implementation @@ -561,84 +596,22 @@ public class BasicMenuItemUI extends MenuItemUI private void paintIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc) { - if (lh.getIcon() != null) { - Icon icon; - ButtonModel model = lh.getMenuItem().getModel(); - if (!model.isEnabled()) { - icon = lh.getMenuItem().getDisabledIcon(); - } else if (model.isPressed() && model.isArmed()) { - icon = lh.getMenuItem().getPressedIcon(); - if (icon == null) { - // Use default icon - icon = lh.getMenuItem().getIcon(); - } - } else { - icon = lh.getMenuItem().getIcon(); - } - - if (icon != null) { - icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, - lr.getIconRect().y); - g.setColor(holdc); - } - } + SwingUtilities3.paintIcon(g, lh, lr, holdc); } private void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc, Color foreground) { - if (lh.getCheckIcon() != null) { - ButtonModel model = lh.getMenuItem().getModel(); - if (model.isArmed() || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(foreground); - } else { - g.setColor(holdc); - } - if (lh.useCheckAndArrow()) { - lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, - lr.getCheckRect().x, lr.getCheckRect().y); - } - g.setColor(holdc); - } + SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); } private void paintAccText(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr) { - if (!lh.getAccText().equals("")) { - ButtonModel model = lh.getMenuItem().getModel(); - g.setFont(lh.getAccFontMetrics().getFont()); - if (!model.isEnabled()) { - // *** paint the accText disabled - if (disabledForeground != null) { - g.setColor(disabledForeground); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x, - lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); - } else { - g.setColor(lh.getMenuItem().getBackground().brighter()); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x, - lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); - g.setColor(lh.getMenuItem().getBackground().darker()); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x - 1, - lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); - } - } else { - // *** paint the accText normally - if (model.isArmed() - || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(acceleratorSelectionForeground); - } else { - g.setColor(acceleratorForeground); - } - SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), - lr.getAccRect().x, lr.getAccRect().y + - lh.getAccFontMetrics().getAscent()); - } - } + SwingUtilities3.setDisabledForeground(disabledForeground); + SwingUtilities3.setAcceleratorSelectionForeground( + acceleratorSelectionForeground); + SwingUtilities3.setAcceleratorForeground(acceleratorForeground); + SwingUtilities3.paintAccText(g, lh, lr); } private void paintText(Graphics g, MenuItemLayoutHelper lh, @@ -657,26 +630,11 @@ public class BasicMenuItemUI extends MenuItemUI private void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color foreground) { - if (lh.getArrowIcon() != null) { - ButtonModel model = lh.getMenuItem().getModel(); - if (model.isArmed() || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(foreground); - } - if (lh.useCheckAndArrow()) { - lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, - lr.getArrowRect().x, lr.getArrowRect().y); - } - } + SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); } private void applyInsets(Rectangle rect, Insets insets) { - if(insets != null) { - rect.x += insets.left; - rect.y += insets.top; - rect.width -= (insets.right + rect.x); - rect.height -= (insets.bottom + rect.y); - } + SwingUtilities3.applyInsets(rect, insets); } /** diff --git a/jdk/test/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java b/jdk/test/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java new file mode 100644 index 0000000000..7fa7e5dd0a --- /dev/null +++ b/jdk/test/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2025, 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 8348760 + * @summary Verify if RadioButtonMenuItem bullet and + * JCheckboxMenuItem checkmark is shown if + * JRadioButtonMenuItem and JCheckboxMenuItem + * is rendered with ImageIcon in WindowsLookAndFeel + * @requires (os.family == "windows") + * @run main/manual TestRadioAndCheckMenuItemWithIcon + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.util.concurrent.LinkedBlockingQueue; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class TestRadioAndCheckMenuItemWithIcon { + + private static final String INSTRUCTIONS = + "Clicking on the Menu above will show a\n" + + "JRadioButtonMenuItem group with 3 radiobutton menuitems\n" + + "and a JCheckBoxMenuItem group with 3 checkbox menuitems.\n" + + "\n" + + "First radiobutton menuitem is selected with imageicon of a red square.\n" + + "Second radiobutton menuitem is unselected with imageicon.\n" + + "Third radiobutton menuItem is unselected without imageicon.\n" + + "\n" + + "First checkbox menuitem is selected with imageicon.\n" + + "Second checkbox menuitem is unselected with imageicon.\n" + + "Third checkbox menuItem is unselected without imageicon.\n" + + "\n" + + "Verify that for first JRadioButtonMenuItem with imageicon,\n" + + "a bullet is shown alongside the imageicon and\n" + + "for first JCheckBoxMenuItem with imageicon\n" + + "a checkmark is shown alongside the imageicon.\n" + + "\n" + + "If bullet and checkmark is shown, test passes else fails."; + + private static final LinkedBlockingQueue resultQueue = new LinkedBlockingQueue(1); + + private static void endTest(boolean result) { + SwingUtilities.invokeLater( + new Runnable() { + public void run() { + try { + resultQueue.put(result); + } catch (Exception ignored) { + } + } + }); + } + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + TestRadioAndCheckMenuItemWithIcon.doTest(); + } + }); + + Boolean testPasses = resultQueue.take(); + + if (!testPasses) { + throw new Exception("Test failed!"); + } + } + + public static void doTest() { + BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + Graphics g = img.getGraphics(); + g.setColor(Color.red); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + g.dispose(); + + BufferedImage img2 = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + Graphics g2 = img2.getGraphics(); + g2.setColor(Color.red); + g2.fillRect(0, 0, img2.getWidth(), img2.getHeight()); + g2.dispose(); + + JFrame frame = new JFrame("RadioButtonWithImageIcon"); + ImageIcon imageIcon1 = new ImageIcon(img); + ImageIcon imageIcon2 = new ImageIcon(img2); + AbstractButton button1; + JRadioButtonMenuItem m1 = new JRadioButtonMenuItem("JRadioButtonMenuItem 1", + imageIcon1); + m1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, ActionEvent.ALT_MASK|ActionEvent.CTRL_MASK|ActionEvent.SHIFT_MASK)); + button1 = m1; + button1.setSelected(true); + AbstractButton button2 = new JRadioButtonMenuItem("JRadioButtonMenuItem 2", imageIcon2); + AbstractButton button3 = new JRadioButtonMenuItem("JRadioButtonMenuItem 3"); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(button1); + buttonGroup.add(button2); + buttonGroup.add(button3); + + AbstractButton check1 = new JCheckBoxMenuItem("JCheckBoxMenuItem 1", + imageIcon1); + check1.setSelected(true); + AbstractButton check2 = new JCheckBoxMenuItem("JCheckBoxMenuItem 2", imageIcon1); + JCheckBoxMenuItem c3; + AbstractButton check3 = c3 = new JCheckBoxMenuItem("JCheckBoxMenuItem 3"); + c3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, ActionEvent.ALT_MASK|ActionEvent.CTRL_MASK|ActionEvent.SHIFT_MASK)); + + JMenu topLevel = new JMenu("Menu"); + + topLevel.add(button1); + topLevel.add(button2); + topLevel.add(button3); + + topLevel.addSeparator(); + + topLevel.add(check1); + topLevel.add(check2); + topLevel.add(check3); + + AbstractButton menuitem1 = new JMenuItem("MenuItem1"); + AbstractButton menuitem2 = new JMenuItem("MenuItem2", imageIcon1); + topLevel.addSeparator(); + topLevel.add(menuitem1); + topLevel.add(menuitem2); + + JMenuBar menuBar = new JMenuBar(); + menuBar.add(topLevel); + + frame.setJMenuBar(menuBar); + + JTextArea instructions = new JTextArea(); + instructions.setText(INSTRUCTIONS); + instructions.setEditable(false); + instructions.setColumns(80); + instructions.setRows(24); + + JScrollPane scrInstructions = new JScrollPane(instructions); + frame.getContentPane().setLayout(new BorderLayout()); + + frame.getContentPane().add(scrInstructions, BorderLayout.CENTER); + + JPanel yesno = new JPanel(); + yesno.setLayout(new GridLayout(1, 2, 40, 40)); + + JButton yes = new JButton("Passes"); + JButton no = new JButton("Fails"); + yesno.add(yes); + yesno.add(no); + + yes.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + frame.dispose(); + endTest(true); + } + }); + + no.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + frame.dispose(); + endTest(false); + } + }); + + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + frame.dispose(); + endTest(false); + } + }); + + frame.getContentPane().add(yesno, BorderLayout.SOUTH); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } +} From 2e6cbc976f558ccbf004f2803d82be70ad20dfde Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 26 Sep 2025 15:47:15 +0000 Subject: [PATCH 2/6] 8368308: ISO 4217 Amendment 180 Update Reviewed-by: andrew Backport-of: 85f5bf3f415cc3d44d1618ec574e73f846bb91c4 --- .../share/classes/java/util/CurrencyData.properties | 8 ++++---- jdk/test/java/util/Currency/ISO4217-list-one.txt | 12 ++++++------ jdk/test/java/util/Currency/ValidateISO4217.java | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jdk/src/share/classes/java/util/CurrencyData.properties b/jdk/src/share/classes/java/util/CurrencyData.properties index 4b87809f4b..7d1b74b5d1 100644 --- a/jdk/src/share/classes/java/util/CurrencyData.properties +++ b/jdk/src/share/classes/java/util/CurrencyData.properties @@ -32,7 +32,7 @@ formatVersion=3 # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=179 +dataVersion=180 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -147,7 +147,7 @@ IO=USD # BRUNEI DARUSSALAM BN=BND # BULGARIA -BG=BGN +BG=BGN;2025-12-31-22-00-00;EUR # BURKINA FASO BF=XOF # BURUNDI @@ -193,7 +193,7 @@ HR=EUR # CUBA CU=CUP # Cura\u00e7ao -CW=ANG;2025-04-01-04-00-00;XCG +CW=XCG # CYPRUS CY=EUR # CZECHIA @@ -508,7 +508,7 @@ SR=SRD # SVALBARD AND JAN MAYEN SJ=NOK # Sint Maarten (Dutch part) -SX=ANG;2025-04-01-04-00-00;XCG +SX=XCG # ESWATINI SZ=SZL # SWEDEN diff --git a/jdk/test/java/util/Currency/ISO4217-list-one.txt b/jdk/test/java/util/Currency/ISO4217-list-one.txt index aa8c272589..ca4bfd6dc9 100644 --- a/jdk/test/java/util/Currency/ISO4217-list-one.txt +++ b/jdk/test/java/util/Currency/ISO4217-list-one.txt @@ -1,12 +1,12 @@ # # -# Amendments up until ISO 4217 AMENDMENT NUMBER 179 -# (As of 02 May 2025) +# Amendments up until ISO 4217 AMENDMENT NUMBER 180 +# (As of 22 September 2025) # # Version FILEVERSION=3 -DATAVERSION=179 +DATAVERSION=180 # ISO 4217 currency data AF AFN 971 2 @@ -44,7 +44,7 @@ BV NOK 578 2 BR BRL 986 2 IO USD 840 2 BN BND 96 2 -BG BGN 975 2 +BG BGN 975 2 2025-12-31-22-00-00 EUR 978 2 BF XOF 952 0 BI BIF 108 0 KH KHR 116 2 @@ -69,7 +69,7 @@ CR CRC 188 2 CI XOF 952 0 HR EUR 978 2 CU CUP 192 2 -CW ANG 532 2 2025-04-01-04-00-00 XCG 532 2 +CW XCG 532 2 CY EUR 978 2 CZ CZK 203 2 DK DKK 208 2 @@ -233,7 +233,7 @@ LK LKR 144 2 SD SDG 938 2 SR SRD 968 2 SJ NOK 578 2 -SX ANG 532 2 2025-04-01-04-00-00 XCG 532 2 +SX XCG 532 2 SZ SZL 748 2 SE SEK 752 2 CH CHF 756 2 diff --git a/jdk/test/java/util/Currency/ValidateISO4217.java b/jdk/test/java/util/Currency/ValidateISO4217.java index 50ef6c15d4..9e49a44f9e 100644 --- a/jdk/test/java/util/Currency/ValidateISO4217.java +++ b/jdk/test/java/util/Currency/ValidateISO4217.java @@ -25,7 +25,7 @@ * @bug 4691089 4819436 4942982 5104960 6544471 6627549 7066203 7195759 * 8074350 8074351 8145952 8187946 8193552 8202026 8204269 * 8208746 8209775 8274658 8283277 8296239 8321480 8334653 - * 8356096 + * 8356096 8368308 * @summary Validate ISO 4217 data for Currency class. */ @@ -95,7 +95,7 @@ public class ValidateISO4217 { /* Codes that are obsolete, do not have related country, extra currency */ static final String otherCodes = - "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" + "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BGN-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" + "DEM-EEK-ESP-FIM-FRF-GHC-GRD-GWP-HRK-IEP-ITL-LTL-LUF-LVL-MGF-MRO-MTL-MXV-MZM-NLG-" + "PTE-ROL-RUR-SDD-SIT-SLL-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-VED-" + "XAD-XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" From 5cffbcb0344f2cf16682a09519894ba705182241 Mon Sep 17 00:00:00 2001 From: Andrew John Hughes Date: Fri, 26 Sep 2025 15:53:13 +0000 Subject: [PATCH 3/6] 8365389: Remove static color fields from SwingUtilities3 and WindowsMenuItemUI Reviewed-by: sgehwolf Backport-of: 3468c6e5ef7e7592cf9484736ce333fbe0eaf34d --- .../com/sun/java/swing/SwingUtilities3.java | 21 ++------ .../windows/WindowsCheckBoxMenuItemUI.java | 4 +- .../swing/plaf/windows/WindowsMenuItemUI.java | 52 +++++-------------- .../swing/plaf/windows/WindowsMenuUI.java | 3 +- .../windows/WindowsRadioButtonMenuItemUI.java | 4 +- .../swing/plaf/basic/BasicMenuItemUI.java | 9 ++-- 6 files changed, 28 insertions(+), 65 deletions(-) diff --git a/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java b/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java index c29297a630..624733ac41 100644 --- a/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/jdk/src/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -70,10 +70,6 @@ public class SwingUtilities3 { private static final Object DELEGATE_REPAINT_MANAGER_KEY = new StringBuilder("DelegateRepaintManagerKey"); - private static Color disabledForeground; - private static Color acceleratorSelectionForeground; - private static Color acceleratorForeground; - /** * Registers delegate RepaintManager for {@code JComponent}. */ @@ -181,7 +177,10 @@ public class SwingUtilities3 { public static void paintAccText(Graphics g, MenuItemLayoutHelper lh, - MenuItemLayoutHelper.LayoutResult lr) { + MenuItemLayoutHelper.LayoutResult lr, + Color disabledForeground, + Color acceleratorSelectionForeground, + Color acceleratorForeground) { if (!lh.getAccText().isEmpty()) { ButtonModel model = lh.getMenuItem().getModel(); g.setFont(lh.getAccFontMetrics().getFont()); @@ -220,18 +219,6 @@ public class SwingUtilities3 { } } - public static void setDisabledForeground(Color disabledFg) { - disabledForeground = disabledFg; - } - - public static void setAcceleratorSelectionForeground(Color acceleratorSelectionFg) { - acceleratorSelectionForeground = acceleratorSelectionFg; - } - - public static void setAcceleratorForeground(Color acceleratorFg) { - acceleratorForeground = acceleratorFg; - } - public static void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color foreground) { diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index fdf109c9dd..9efdc860d6 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -84,7 +84,9 @@ public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { int defaultTextIconGap) { if (WindowsMenuItemUI.isVistaPainting()) { WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, - arrowIcon, background, foreground, defaultTextIconGap, + arrowIcon, background, foreground, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, menuItem, getPropertyPrefix()); return; } diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 41995f322d..c74f0490ee 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -51,10 +51,6 @@ import com.sun.java.swing.SwingUtilities3; */ public class WindowsMenuItemUI extends BasicMenuItemUI { - private static Color disabledForeground; - private static Color acceleratorSelectionForeground; - private static Color acceleratorForeground; - final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { @@ -96,36 +92,6 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { } - private static void applyInsets(Rectangle rect, Insets insets) { - SwingUtilities3.applyInsets(rect, insets); - } - - private static void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, - MenuItemLayoutHelper.LayoutResult lr, - Color holdc, Color foreground) { - SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); - } - - private static void paintIcon(Graphics g, MenuItemLayoutHelper lh, - MenuItemLayoutHelper.LayoutResult lr, Color holdc) { - SwingUtilities3.paintIcon(g, lh, lr, holdc); - } - - private static void paintAccText(Graphics g, MenuItemLayoutHelper lh, - MenuItemLayoutHelper.LayoutResult lr) { - SwingUtilities3.setDisabledForeground(disabledForeground); - SwingUtilities3.setAcceleratorSelectionForeground( - acceleratorSelectionForeground); - SwingUtilities3.setAcceleratorForeground(acceleratorForeground); - SwingUtilities3.paintAccText(g, lh, lr); - } - - private static void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, - MenuItemLayoutHelper.LayoutResult lr, - Color foreground) { - SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); - } - protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, @@ -133,7 +99,8 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { if (WindowsMenuItemUI.isVistaPainting()) { WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, arrowIcon, background, foreground, - defaultTextIconGap, menuItem, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, menuItem, getPropertyPrefix()); return; } @@ -144,6 +111,9 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, + Color disabledForeground, + Color acceleratorSelectionForeground, + Color acceleratorForeground, int defaultTextIconGap, JMenuItem menuItem, String prefix) { // Save original graphics font and color Font holdf = g.getFont(); @@ -153,7 +123,7 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { g.setFont(mi.getFont()); Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); - applyInsets(viewRect, mi.getInsets()); + SwingUtilities3.applyInsets(viewRect, mi.getInsets()); String acceleratorDelimiter = UIManager.getString("MenuItem.acceleratorDelimiter"); @@ -171,8 +141,8 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); paintBackground(accessor, g, mi, background); - paintCheckIcon(g, lh, lr, holdc, foreground); - paintIcon(g, lh, lr, holdc); + SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); + SwingUtilities3.paintIcon(g, lh, lr, holdc); if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { Rectangle rect = lr.getTextRect(); @@ -196,8 +166,10 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { rect.x += lh.getAfterCheckIconGap(); lr.setAccRect(rect); } - paintAccText(g, lh, lr); - paintArrowIcon(g, lh, lr, foreground); + SwingUtilities3.paintAccText(g, lh, lr, disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground); + SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); // Restore original graphics font and color g.setColor(holdc); diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index d81e172a28..c5bd54edf2 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -133,7 +133,8 @@ public class WindowsMenuUI extends BasicMenuUI { if (WindowsMenuItemUI.isVistaPainting()) { WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, arrowIcon, background, foreground, - defaultTextIconGap, menuItem, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, menuItem, getPropertyPrefix()); return; } diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index 5500791603..41bcfb7b86 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -83,7 +83,9 @@ public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { int defaultTextIconGap) { if (WindowsMenuItemUI.isVistaPainting()) { WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, - arrowIcon, background, foreground, defaultTextIconGap, + arrowIcon, background, foreground, + disabledForeground, acceleratorSelectionForeground, + acceleratorForeground, defaultTextIconGap, menuItem, getPropertyPrefix()); return; } diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java index 0d45bb54f0..0467cf11d4 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -607,11 +607,10 @@ public class BasicMenuItemUI extends MenuItemUI private void paintAccText(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr) { - SwingUtilities3.setDisabledForeground(disabledForeground); - SwingUtilities3.setAcceleratorSelectionForeground( - acceleratorSelectionForeground); - SwingUtilities3.setAcceleratorForeground(acceleratorForeground); - SwingUtilities3.paintAccText(g, lh, lr); + SwingUtilities3.paintAccText(g, lh, lr, + disabledForeground, + acceleratorSelectionForeground, + acceleratorForeground); } private void paintText(Graphics g, MenuItemLayoutHelper lh, From 327303315367f15113c17c8525d8eb3bc0f66782 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Wed, 10 Sep 2025 18:12:18 -0700 Subject: [PATCH 4/6] 8352637: Enhance bytecode verification Reviewed-by: fferrari, andrew Backport-of: 974f4da2e53ebde8b06224f4ba0b80aa74c5f434 --- .../src/share/vm/classfile/stackMapTable.cpp | 10 ++++- .../src/share/vm/classfile/stackMapTable.hpp | 2 +- hotspot/src/share/vm/classfile/verifier.cpp | 35 +++++++++-------- .../share/vm/interpreter/bytecodeStream.hpp | 19 ++++++++- jdk/src/share/native/common/check_code.c | 39 +++++++++++++------ 5 files changed, 74 insertions(+), 31 deletions(-) diff --git a/hotspot/src/share/vm/classfile/stackMapTable.cpp b/hotspot/src/share/vm/classfile/stackMapTable.cpp index 547dcf64b9..13606ae161 100644 --- a/hotspot/src/share/vm/classfile/stackMapTable.cpp +++ b/hotspot/src/share/vm/classfile/stackMapTable.cpp @@ -122,8 +122,16 @@ bool StackMapTable::match_stackmap( } void StackMapTable::check_jump_target( - StackMapFrame* frame, int32_t target, TRAPS) const { + StackMapFrame* frame, int bci, int offset, TRAPS) const { ErrorContext ctx; + // Jump targets must be within the method and the method size is limited. See JVMS 4.11 + int min_offset = -1 * max_method_code_size; + if (offset < min_offset || offset > max_method_code_size) { + frame->verifier()->verify_error(ErrorContext::bad_stackmap(bci, frame), + "Illegal target of jump or branch (bci %d + offset %d)", bci, offset); + return; + } + int target = bci + offset; bool match = match_stackmap( frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier())); if (!match || (target < 0 || target >= _code_length)) { diff --git a/hotspot/src/share/vm/classfile/stackMapTable.hpp b/hotspot/src/share/vm/classfile/stackMapTable.hpp index a36a7ba3cf..36fddb3988 100644 --- a/hotspot/src/share/vm/classfile/stackMapTable.hpp +++ b/hotspot/src/share/vm/classfile/stackMapTable.hpp @@ -86,7 +86,7 @@ class StackMapTable : public StackObj { // Check jump instructions. Make sure there are no uninitialized // instances on backward branch. - void check_jump_target(StackMapFrame* frame, int32_t target, TRAPS) const; + void check_jump_target(StackMapFrame* frame, int bci, int offset, TRAPS) const; // The following methods are only used inside this class. diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp index 2dddd1fded..4662bf02a5 100644 --- a/hotspot/src/share/vm/classfile/verifier.cpp +++ b/hotspot/src/share/vm/classfile/verifier.cpp @@ -668,7 +668,6 @@ void ClassVerifier::verify_method(methodHandle m, TRAPS) { // Merge with the next instruction { u2 index; - int target; VerificationType type, type2; VerificationType atype; @@ -1480,9 +1479,8 @@ void ClassVerifier::verify_method(methodHandle m, TRAPS) { case Bytecodes::_ifle: current_frame.pop_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_if_acmpeq : case Bytecodes::_if_acmpne : @@ -1493,19 +1491,16 @@ void ClassVerifier::verify_method(methodHandle m, TRAPS) { case Bytecodes::_ifnonnull : current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); - target = bcs.dest(); stackmap_table.check_jump_target - (¤t_frame, target, CHECK_VERIFY(this)); + (¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_goto : - target = bcs.dest(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_goto_w : - target = bcs.dest_w(); stackmap_table.check_jump_target( - ¤t_frame, target, CHECK_VERIFY(this)); + ¤t_frame, bcs.bci(), bcs.get_offset_s4(), CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_tableswitch : case Bytecodes::_lookupswitch : @@ -2107,15 +2102,14 @@ void ClassVerifier::verify_switch( } } } - int target = bci + default_offset; - stackmap_table->check_jump_target(current_frame, target, CHECK_VERIFY(this)); + stackmap_table->check_jump_target(current_frame, bci, default_offset, CHECK_VERIFY(this)); for (int i = 0; i < keys; i++) { // Because check_jump_target() may safepoint, the bytecode could have // moved, which means 'aligned_bcp' is no good and needs to be recalculated. aligned_bcp = (address)round_to((intptr_t)(bcs->bcp() + 1), jintSize); - target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); stackmap_table->check_jump_target( - current_frame, target, CHECK_VERIFY(this)); + current_frame, bci, offset, CHECK_VERIFY(this)); } NOT_PRODUCT(aligned_bcp = NULL); // no longer valid at this point } @@ -2376,8 +2370,13 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) { break; case Bytecodes::_goto: - case Bytecodes::_goto_w: - target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); + case Bytecodes::_goto_w: { + int offset = (opcode == Bytecodes::_goto ? bcs.get_offset_s2() : bcs.get_offset_s4()); + int min_offset = -1 * max_method_code_size; + // Check offset for overflow + if (offset < min_offset || offset > max_method_code_size) return false; + + target = bci + offset; if (visited_branches->contains(bci)) { if (bci_stack->is_empty()) { if (handler_stack->is_empty()) { @@ -2398,6 +2397,7 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) { visited_branches->append(bci); } break; + } // Check that all switch alternatives end in 'athrow' bytecodes. Since it // is difficult to determine where each switch alternative ends, parse @@ -2434,7 +2434,10 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) { // Push the switch alternatives onto the stack. for (int i = 0; i < keys; i++) { - u4 target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + int min_offset = -1 * max_method_code_size; + int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + if (offset < min_offset || offset > max_method_code_size) return false; + u4 target = bci + offset; if (target > code_length) return false; bci_stack->push(target); } diff --git a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp index b814b88d5d..20f38950e1 100644 --- a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp +++ b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp @@ -121,8 +121,23 @@ class BaseBytecodeStream: StackObj { void set_next_bci(int bci) { assert(0 <= bci && bci <= method()->code_size(), "illegal bci"); _next_bci = bci; } // Bytecode-specific attributes - int dest() const { return bci() + bytecode().get_offset_s2(raw_code()); } - int dest_w() const { return bci() + bytecode().get_offset_s4(raw_code()); } + int get_offset_s2() const { return bytecode().get_offset_s2(raw_code()); } + int get_offset_s4() const { return bytecode().get_offset_s4(raw_code()); } + + // These methods are not safe to use before or during verification as they may + // have large offsets and cause overflows + int dest() const { + int min_offset = -1 * max_method_code_size; + int offset = bytecode().get_offset_s2(raw_code()); + guarantee(offset >= min_offset && offset <= max_method_code_size, "must be"); + return bci() + offset; + } + int dest_w() const { + int min_offset = -1 * max_method_code_size; + int offset = bytecode().get_offset_s4(raw_code()); + guarantee(offset >= min_offset && offset <= max_method_code_size, "must be"); + return bci() + offset; + } // One-byte indices. int get_index_u1() const { assert_raw_index_size(1); return *(jubyte*)(bcp()+1); } diff --git a/jdk/src/share/native/common/check_code.c b/jdk/src/share/native/common/check_code.c index 799b8c654c..0889c57f49 100644 --- a/jdk/src/share/native/common/check_code.c +++ b/jdk/src/share/native/common/check_code.c @@ -396,7 +396,8 @@ static jboolean is_superclass(context_type *, fullinfo_type); static void initialize_exception_table(context_type *); static int instruction_length(unsigned char *iptr, unsigned char *end); -static jboolean isLegalTarget(context_type *, int offset); +static jboolean isLegalOffset(context_type *, int bci, int offset); +static jboolean isLegalTarget(context_type *, int target); static void verify_constant_pool_type(context_type *, int, unsigned); static void initialize_dataflow(context_type *); @@ -1161,9 +1162,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) case JVM_OPC_goto: { /* Set the ->operand to be the instruction number of the target. */ int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2]; - int target = offset + jump; - if (!isLegalTarget(context, target)) + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal target of jump or branch"); + int target = offset + jump; this_idata->operand.i = code_data[target]; break; } @@ -1177,9 +1178,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) int jump = (((signed char)(code[offset+1])) << 24) + (code[offset+2] << 16) + (code[offset+3] << 8) + (code[offset + 4]); - int target = offset + jump; - if (!isLegalTarget(context, target)) + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal target of jump or branch"); + int target = offset + jump; this_idata->operand.i = code_data[target]; break; } @@ -1218,13 +1219,16 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) } } saved_operand = NEW(int, keys + 2); - if (!isLegalTarget(context, offset + _ck_ntohl(lpc[0]))) + int jump = _ck_ntohl(lpc[0]); + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal default target in switch"); - saved_operand[keys + 1] = code_data[offset + _ck_ntohl(lpc[0])]; + int target = offset + jump; + saved_operand[keys + 1] = code_data[target]; for (k = keys, lptr = &lpc[3]; --k >= 0; lptr += delta) { - int target = offset + _ck_ntohl(lptr[0]); - if (!isLegalTarget(context, target)) + jump = _ck_ntohl(lptr[0]); + if (!isLegalOffset(context, offset, jump)) CCerror(context, "Illegal branch in tableswitch"); + target = offset + jump; saved_operand[k + 1] = code_data[target]; } saved_operand[0] = keys + 1; /* number of successors */ @@ -1746,11 +1750,24 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) /* Given the target of a branch, make sure that it's a legal target. */ static jboolean -isLegalTarget(context_type *context, int offset) +isLegalTarget(context_type *context, int target) { int code_length = context->code_length; int *code_data = context->code_data; - return (offset >= 0 && offset < code_length && code_data[offset] >= 0); + return (target >= 0 && target < code_length && code_data[target] >= 0); +} + +/* Given a bci and offset, make sure the offset is valid and the target is legal */ +static jboolean +isLegalOffset(context_type *context, int bci, int offset) +{ + int code_length = context->code_length; + int *code_data = context->code_data; + int max_offset = 65535; // JVMS 4.11 + int min_offset = -65535; + if (offset < min_offset || offset > max_offset) return JNI_FALSE; + int target = bci + offset; + return (target >= 0 && target < code_length && code_data[target] >= 0); } From dde48aedce2fe649e1737b3eec829428381bcb8f Mon Sep 17 00:00:00 2001 From: Aleksei Voitylov Date: Wed, 3 Sep 2025 01:47:08 +0000 Subject: [PATCH 5/6] 8356294: Enhance Path Factories Reviewed-by: abakhtin, fferrari, andrew Backport-of: 2c7f45612d11199a9d5eaa6d61a2893ec4afa687 --- .../xpath/internal/jaxp/XPathFactoryImpl.java | 1 + .../apache/xpath/internal/jaxp/XPathImpl.java | 5 +++++ .../src/jdk/xml/internal/XMLSecurityManager.java | 16 ++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java index 0f1a2262c3..217ad54b78 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java @@ -187,6 +187,7 @@ public class XPathFactoryImpl extends XPathFactory { if (value && _featureManager != null) { _featureManager.setFeature(JdkXmlFeatures.XmlFeature.ENABLE_EXTENSION_FUNCTION, JdkXmlFeatures.State.FSP, false); + _xmlSecMgr.setSecureProcessing(value); } // all done processing feature diff --git a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java index 8d15deaad3..cc82576670 100644 --- a/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java +++ b/jaxp/src/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java @@ -20,6 +20,7 @@ package com.sun.org.apache.xpath.internal.jaxp; +import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.namespace.NamespaceContext; import javax.xml.xpath.XPathExpressionException; @@ -180,6 +181,10 @@ public class XPathImpl implements javax.xml.xpath.XPath { // so we really have to create a fresh DocumentBuilder every time we need one // - KK DocumentBuilderFactory dbf = JdkXmlUtils.getDOMFactory(overrideDefaultParser); + if (xmlSecMgr != null && xmlSecMgr.isSecureProcessingSet()) { + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, + xmlSecMgr.isSecureProcessing()); + } return dbf.newDocumentBuilder(); } catch (ParserConfigurationException e) { // this should never happen with a well-behaving JAXP implementation. diff --git a/jaxp/src/jdk/xml/internal/XMLSecurityManager.java b/jaxp/src/jdk/xml/internal/XMLSecurityManager.java index 2dd165757f..64de41f51f 100644 --- a/jaxp/src/jdk/xml/internal/XMLSecurityManager.java +++ b/jaxp/src/jdk/xml/internal/XMLSecurityManager.java @@ -189,6 +189,12 @@ public final class XMLSecurityManager { */ boolean secureProcessing; + /** + * Flag indicating the secure processing is set explicitly through factories' + * setFeature method and then the setSecureProcessing method + */ + boolean secureProcessingSet; + /** * States that determine if properties are set explicitly */ @@ -236,6 +242,7 @@ public final class XMLSecurityManager { * Setting FEATURE_SECURE_PROCESSING explicitly */ public void setSecureProcessing(boolean secure) { + secureProcessingSet = true; secureProcessing = secure; for (Limit limit : Limit.values()) { if (secure) { @@ -254,6 +261,15 @@ public final class XMLSecurityManager { return secureProcessing; } + /** + * Returns the state indicating whether the Secure Processing is set explicitly, + * via factories' setFeature and then this class' setSecureProcessing method. + * @return the state indicating whether the Secure Processing is set explicitly + */ + public boolean isSecureProcessingSet() { + return secureProcessingSet; + } + /** * Set limit by property name and state * @param propertyName property name From d5ac2ad89a369697a48e7f3e6b889e22afa50a2f Mon Sep 17 00:00:00 2001 From: Alexey Bakhtin Date: Wed, 27 Aug 2025 13:50:17 -0700 Subject: [PATCH 6/6] 8360937: Enhance certificate handling Reviewed-by: fferrari, andrew Backport-of: d3b1c2be9e87aad07cac29d94679130fe5807c17 --- .../classes/sun/security/util/DerValue.java | 16 ++++++ .../share/classes/sun/security/x509/AVA.java | 55 ++++++++++++++++++- .../testlibrary/CertificateBuilder.java | 29 ++++++++-- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/jdk/src/share/classes/sun/security/util/DerValue.java b/jdk/src/share/classes/sun/security/util/DerValue.java index b7d1714769..2dd262ba99 100644 --- a/jdk/src/share/classes/sun/security/util/DerValue.java +++ b/jdk/src/share/classes/sun/security/util/DerValue.java @@ -822,6 +822,22 @@ public class DerValue { doEquals(other, this); } + /** + * Checks that the BMPString does not contain any surrogate characters, + * which are outside the Basic Multilingual Plane. + * + * @throws IOException if illegal characters are detected + */ + public void validateBMPString() throws IOException { + String bmpString = getBMPString(); + for (int i = 0; i < bmpString.length(); i++) { + if (Character.isSurrogate(bmpString.charAt(i))) { + throw new IOException( + "Illegal character in BMPString, index: " + i); + } + } + } + /** * Helper for public method equals() */ diff --git a/jdk/src/share/classes/sun/security/x509/AVA.java b/jdk/src/share/classes/sun/security/x509/AVA.java index 8665745c35..f896b8d6e8 100644 --- a/jdk/src/share/classes/sun/security/x509/AVA.java +++ b/jdk/src/share/classes/sun/security/x509/AVA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, 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 @@ -30,9 +30,14 @@ import java.io.IOException; import java.io.OutputStream; import java.io.Reader; import java.security.AccessController; +import java.nio.charset.Charset; import java.text.Normalizer; import java.util.*; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_16BE; + import sun.security.action.GetBooleanAction; import sun.security.util.*; import sun.security.pkcs.PKCS9Attribute; @@ -606,6 +611,10 @@ public class AVA implements DerEncoder { throw new IOException("AVA, extra bytes = " + derval.data.available()); } + + if (value.tag == DerValue.tag_BMPString) { + value.validateBMPString(); + } } AVA(DerInputStream in) throws IOException { @@ -753,7 +762,7 @@ public class AVA implements DerEncoder { */ String valStr = null; try { - valStr = new String(value.getDataBytes(), "UTF8"); + valStr = new String(value.getDataBytes(), getCharset(value, false)); } catch (IOException ie) { throw new IllegalArgumentException("DER Value conversion"); } @@ -906,7 +915,7 @@ public class AVA implements DerEncoder { */ String valStr = null; try { - valStr = new String(value.getDataBytes(), "UTF8"); + valStr = new String(value.getDataBytes(), getCharset(value, true)); } catch (IOException ie) { throw new IllegalArgumentException("DER Value conversion"); } @@ -1026,6 +1035,46 @@ public class AVA implements DerEncoder { } } + /* + * Returns the charset that should be used to decode each DN string type. + * + * This method ensures that multi-byte (UTF8String and BMPString) types + * are decoded using the correct charset and the String forms represent + * the correct characters. For 8-bit ASCII-based types (PrintableString + * and IA5String), we return ISO_8859_1 rather than ASCII, so that the + * complete range of characters can be represented, as many certificates + * do not comply with the Internationalized Domain Name ACE format. + * + * NOTE: this method only supports DirectoryStrings of the types returned + * by isDerString(). + */ + private static Charset getCharset(DerValue value, boolean canonical) { + if (canonical) { + switch (value.tag) { + case DerValue.tag_PrintableString: + return ISO_8859_1; + case DerValue.tag_UTF8String: + return UTF_8; + default: + throw new Error("unexpected tag: " + value.tag); + } + } + + switch (value.tag) { + case DerValue.tag_PrintableString: + case DerValue.tag_T61String: + case DerValue.tag_IA5String: + case DerValue.tag_GeneralString: + return ISO_8859_1; + case DerValue.tag_BMPString: + return UTF_16BE; + case DerValue.tag_UTF8String: + return UTF_8; + default: + throw new Error("unexpected tag: " + value.tag); + } + } + boolean hasRFC2253Keyword() { return AVAKeyword.hasKeyword(oid, RFC2253); } diff --git a/jdk/test/java/security/testlibrary/CertificateBuilder.java b/jdk/test/java/security/testlibrary/CertificateBuilder.java index 4ab26d0d12..b8cb1a73b4 100644 --- a/jdk/test/java/security/testlibrary/CertificateBuilder.java +++ b/jdk/test/java/security/testlibrary/CertificateBuilder.java @@ -53,6 +53,7 @@ import sun.security.x509.SerialNumber; import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.URIName; import sun.security.x509.KeyIdentifier; +import sun.security.x509.X500Name; /** * Helper class that builds and signs X.509 certificates. @@ -89,7 +90,7 @@ import sun.security.x509.KeyIdentifier; public class CertificateBuilder { private final CertificateFactory factory; - private X500Principal subjectName = null; + private X500Name subjectName = null; private BigInteger serialNumber = null; private PublicKey publicKey = null; private Date notBefore = null; @@ -114,8 +115,9 @@ public class CertificateBuilder { * @param name An {@link X500Principal} to be used as the subject name * on this certificate. */ - public void setSubjectName(X500Principal name) { - subjectName = name; + public CertificateBuilder setSubjectName(X500Principal name) { + subjectName = X500Name.asX500Name(name); + return this; } /** @@ -123,8 +125,25 @@ public class CertificateBuilder { * * @param name The subject name in RFC 2253 format */ - public void setSubjectName(String name) { - subjectName = new X500Principal(name); + public CertificateBuilder setSubjectName(String name) { + try { + subjectName = new X500Name(name); + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); + } + return this; + } + + /** + * Set the subject name for the certificate. This method is useful when + * you need more control over the contents of the subject name. + * + * @param name an {@code X500Name} to be used as the subject name + * on this certificate + */ + public CertificateBuilder setSubjectName(X500Name name) { + subjectName = name; + return this; } /**