8041894: [macosx] Test javax/swing/JSpinner/8008657/bug8008657.java failed on Mac

Reviewed-by: serb, alexsch
This commit is contained in:
Avik Niyogi 2016-01-18 14:20:39 +05:30
parent 355c7d8dfe
commit 9578f1aecb
2 changed files with 162 additions and 72 deletions

View File

@ -22,7 +22,6 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.apple.laf;
import java.awt.*;
@ -45,33 +44,37 @@ import com.apple.laf.AquaUtils.RecyclableSingleton;
import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor;
/**
* This is originally derived from BasicSpinnerUI, but they made everything private
* so we can't subclass!
* This is originally derived from BasicSpinnerUI, but they made everything
* private so we can't subclass!
*/
public class AquaSpinnerUI extends SpinnerUI {
private static final RecyclableSingleton<? extends PropertyChangeListener> propertyChangeListener
= new RecyclableSingletonFromDefaultConstructor<>(PropertyChangeHandler.class);
static PropertyChangeListener getPropertyChangeListener() {
return propertyChangeListener.get();
}
private static final RecyclableSingleton<ArrowButtonHandler> nextButtonHandler
= new RecyclableSingleton<ArrowButtonHandler>() {
@Override
protected ArrowButtonHandler getInstance() {
return new ArrowButtonHandler("increment", true);
}
};
@Override
protected ArrowButtonHandler getInstance() {
return new ArrowButtonHandler("increment", true);
}
};
static ArrowButtonHandler getNextButtonHandler() {
return nextButtonHandler.get();
}
private static final RecyclableSingleton<ArrowButtonHandler> previousButtonHandler
= new RecyclableSingleton<ArrowButtonHandler>() {
@Override
protected ArrowButtonHandler getInstance() {
return new ArrowButtonHandler("decrement", false);
}
};
@Override
protected ArrowButtonHandler getInstance() {
return new ArrowButtonHandler("decrement", false);
}
};
static ArrowButtonHandler getPreviousButtonHandler() {
return previousButtonHandler.get();
}
@ -92,9 +95,10 @@ public class AquaSpinnerUI extends SpinnerUI {
}
boolean wasOpaque;
@Override
public void installUI(final JComponent c) {
this.spinner = (JSpinner)c;
this.spinner = (JSpinner) c;
installDefaults();
installListeners();
next = createNextButton();
@ -110,8 +114,7 @@ public class AquaSpinnerUI extends SpinnerUI {
installKeyboardActions();
// this doesn't work because JSpinner calls setOpaque(true) directly in it's constructor
// LookAndFeel.installProperty(spinner, "opaque", Boolean.FALSE);
// LookAndFeel.installProperty(spinner, "opaque", Boolean.FALSE);
// ...so we have to handle the is/was opaque ourselves
wasOpaque = spinner.isOpaque();
spinner.setOpaque(false);
@ -208,6 +211,7 @@ public class AquaSpinnerUI extends SpinnerUI {
@SuppressWarnings("serial") // Superclass is not serializable across versions
class TransparentButton extends JButton implements SwingConstants {
boolean interceptRepaints = false;
public TransparentButton() {
@ -219,14 +223,17 @@ public class AquaSpinnerUI extends SpinnerUI {
}
@Override
public void paint(final Graphics g) {}
public void paint(final Graphics g) {
}
@Override
public void repaint() {
// only intercept repaints if we are after this has been initialized
// otherwise we can't talk to our containing class
if (interceptRepaints) {
if (spinPainter == null) return;
if (spinPainter == null) {
return;
}
spinPainter.repaint();
}
super.repaint();
@ -246,7 +253,9 @@ public class AquaSpinnerUI extends SpinnerUI {
}
protected void fixupEditor(final JComponent editor) {
if (!(editor instanceof DefaultEditor)) return;
if (!(editor instanceof DefaultEditor)) {
return;
}
editor.setOpaque(false);
editor.setInheritsPopupMenu(true);
@ -255,7 +264,7 @@ public class AquaSpinnerUI extends SpinnerUI {
editor.setFont(new FontUIResource(spinner.getFont()));
}
final JFormattedTextField editorTextField = ((DefaultEditor)editor).getTextField();
final JFormattedTextField editorTextField = ((DefaultEditor) editor).getTextField();
if (editorTextField.getFont() instanceof UIResource) {
editorTextField.setFont(new FontUIResource(spinner.getFont()));
}
@ -277,7 +286,7 @@ public class AquaSpinnerUI extends SpinnerUI {
child.setEnabled(enabled);
if (child instanceof Container) {
updateEnabledState((Container)child, enabled);
updateEnabledState((Container) child, enabled);
}
}
}
@ -290,13 +299,13 @@ public class AquaSpinnerUI extends SpinnerUI {
private InputMap getInputMap(final int condition) {
if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
return (InputMap)UIManager.get("Spinner.ancestorInputMap");
return (InputMap) UIManager.get("Spinner.ancestorInputMap");
}
return null;
}
private ActionMap getActionMap() {
ActionMap map = (ActionMap)UIManager.get("Spinner.actionMap");
ActionMap map = (ActionMap) UIManager.get("Spinner.actionMap");
if (map == null) {
map = createActionMap();
@ -316,6 +325,7 @@ public class AquaSpinnerUI extends SpinnerUI {
@SuppressWarnings("serial") // Superclass is not serializable across versions
private static class ArrowButtonHandler extends AbstractAction implements MouseListener {
final javax.swing.Timer autoRepeatTimer;
final boolean isNext;
JSpinner spinner = null;
@ -330,9 +340,9 @@ public class AquaSpinnerUI extends SpinnerUI {
private JSpinner eventToSpinner(final AWTEvent e) {
Object src = e.getSource();
while ((src instanceof Component) && !(src instanceof JSpinner)) {
src = ((Component)src).getParent();
src = ((Component) src).getParent();
}
return (src instanceof JSpinner) ? (JSpinner)src : null;
return (src instanceof JSpinner) ? (JSpinner) src : null;
}
@Override
@ -342,13 +352,15 @@ public class AquaSpinnerUI extends SpinnerUI {
spinner = eventToSpinner(e);
}
if (spinner == null) return;
if (spinner == null) {
return;
}
try {
final int calendarField = getCalendarField(spinner);
spinner.commitEdit();
if (calendarField != -1) {
((SpinnerDateModel)spinner.getModel()).setCalendarField(calendarField);
((SpinnerDateModel) spinner.getModel()).setCalendarField(calendarField);
}
final Object value = (isNext) ? spinner.getNextValue() : spinner.getPreviousValue();
if (value != null) {
@ -368,37 +380,46 @@ public class AquaSpinnerUI extends SpinnerUI {
*/
private void select(final JSpinner spinnerComponent) {
final JComponent editor = spinnerComponent.getEditor();
if (!(editor instanceof JSpinner.DateEditor)) return;
if (!(editor instanceof JSpinner.DateEditor)) {
return;
}
final JSpinner.DateEditor dateEditor = (JSpinner.DateEditor)editor;
final JSpinner.DateEditor dateEditor = (JSpinner.DateEditor) editor;
final JFormattedTextField ftf = dateEditor.getTextField();
final Format format = dateEditor.getFormat();
Object value;
if (format == null || (value = spinnerComponent.getValue()) == null) return;
if (format == null || (value = spinnerComponent.getValue()) == null) {
return;
}
final SpinnerDateModel model = dateEditor.getModel();
final DateFormat.Field field = DateFormat.Field.ofCalendarField(model.getCalendarField());
if (field == null) return;
if (field == null) {
return;
}
try {
final AttributedCharacterIterator iterator = format.formatToCharacterIterator(value);
if (!select(ftf, iterator, field) && field == DateFormat.Field.HOUR0) {
select(ftf, iterator, DateFormat.Field.HOUR1);
}
} catch (final IllegalArgumentException iae) {}
} catch (final IllegalArgumentException iae) {
}
}
/**
* Selects the passed in field, returning true if it is found,
* false otherwise.
* Selects the passed in field, returning true if it is found, false
* otherwise.
*/
private boolean select(final JFormattedTextField ftf, final AttributedCharacterIterator iterator, final DateFormat.Field field) {
final int max = ftf.getDocument().getLength();
iterator.first();
do {
final Map<Attribute,Object> attrs = iterator.getAttributes();
if (attrs == null || !attrs.containsKey(field)) continue;
final Map<Attribute, Object> attrs = iterator.getAttributes();
if (attrs == null || !attrs.containsKey(field)) {
continue;
}
final int start = iterator.getRunStart(field);
final int end = iterator.getRunLimit(field);
@ -412,29 +433,35 @@ public class AquaSpinnerUI extends SpinnerUI {
}
/**
* Returns the calendarField under the start of the selection, or
* -1 if there is no valid calendar field under the selection (or
* the spinner isn't editing dates.
* Returns the calendarField under the start of the selection, or -1 if
* there is no valid calendar field under the selection (or the spinner
* isn't editing dates.
*/
private int getCalendarField(final JSpinner spinnerComponent) {
final JComponent editor = spinnerComponent.getEditor();
if (!(editor instanceof JSpinner.DateEditor)) return -1;
if (!(editor instanceof JSpinner.DateEditor)) {
return -1;
}
final JSpinner.DateEditor dateEditor = (JSpinner.DateEditor)editor;
final JSpinner.DateEditor dateEditor = (JSpinner.DateEditor) editor;
final JFormattedTextField ftf = dateEditor.getTextField();
final int start = ftf.getSelectionStart();
final JFormattedTextField.AbstractFormatter formatter = ftf.getFormatter();
if (!(formatter instanceof InternationalFormatter)) return -1;
if (!(formatter instanceof InternationalFormatter)) {
return -1;
}
final Format.Field[] fields = ((InternationalFormatter)formatter).getFields(start);
final Format.Field[] fields = ((InternationalFormatter) formatter).getFields(start);
for (final Field element : fields) {
if (!(element instanceof DateFormat.Field)) continue;
if (!(element instanceof DateFormat.Field)) {
continue;
}
int calendarField;
if (element == DateFormat.Field.HOUR1) {
calendarField = Calendar.HOUR;
} else {
calendarField = ((DateFormat.Field)element).getCalendarField();
calendarField = ((DateFormat.Field) element).getCalendarField();
}
if (calendarField != -1) {
@ -446,7 +473,9 @@ public class AquaSpinnerUI extends SpinnerUI {
@Override
public void mousePressed(final MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e) || !e.getComponent().isEnabled()) return;
if (!SwingUtilities.isLeftMouseButton(e) || !e.getComponent().isEnabled()) {
return;
}
spinner = eventToSpinner(e);
autoRepeatTimer.start();
@ -460,26 +489,35 @@ public class AquaSpinnerUI extends SpinnerUI {
}
@Override
public void mouseClicked(final MouseEvent e) {}
public void mouseClicked(final MouseEvent e) {
}
@Override
public void mouseEntered(final MouseEvent e) {}
public void mouseEntered(final MouseEvent e) {
}
@Override
public void mouseExited(final MouseEvent e) {}
public void mouseExited(final MouseEvent e) {
}
/**
* Requests focus on a child of the spinner if the spinner doesn't
* have focus.
* Requests focus on a child of the spinner if the spinner doesn't have
* focus.
*/
private void focusSpinnerIfNecessary() {
final Component fo = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (!spinner.isRequestFocusEnabled() || (fo != null && (SwingUtilities.isDescendingFrom(fo, spinner)))) return;
if (!spinner.isRequestFocusEnabled() || (fo != null && (SwingUtilities.isDescendingFrom(fo, spinner)))) {
return;
}
Container root = spinner;
if (!root.isFocusCycleRoot()) {
root = root.getFocusCycleRootAncestor();
}
if (root == null) return;
if (root == null) {
return;
}
final FocusTraversalPolicy ftp = root.getFocusTraversalPolicy();
final Component child = ftp.getComponentAfter(root, spinner);
@ -491,6 +529,7 @@ public class AquaSpinnerUI extends SpinnerUI {
@SuppressWarnings("serial") // Superclass is not serializable across versions
class SpinPainter extends JComponent {
final AquaPainter<JRSUIState> painter = AquaPainter.create(JRSUIStateFactory.getSpinnerArrows());
ButtonModel fTopModel;
@ -551,11 +590,12 @@ public class AquaSpinnerUI extends SpinnerUI {
}
/**
* A simple layout manager for the editor and the next/previous buttons.
* See the AquaSpinnerUI javadoc for more information about exactly
* how the components are arranged.
* A simple layout manager for the editor and the next/previous buttons. See
* the AquaSpinnerUI javadoc for more information about exactly how the
* components are arranged.
*/
static class SpinnerLayout implements LayoutManager {
private Component nextButton = null;
private Component previousButton = null;
private Component editor = null;
@ -656,26 +696,38 @@ public class AquaSpinnerUI extends SpinnerUI {
}
/**
* Detect JSpinner property changes we're interested in and delegate. Subclasses
* shouldn't need to replace the default propertyChangeListener (although they
* can by overriding createPropertyChangeListener) since all of the interesting
* property changes are delegated to protected methods.
* Detect JSpinner property changes we're interested in and delegate.
* Subclasses shouldn't need to replace the default propertyChangeListener
* (although they can by overriding createPropertyChangeListener) since all
* of the interesting property changes are delegated to protected methods.
*/
static class PropertyChangeHandler implements PropertyChangeListener {
@Override
public void propertyChange(final PropertyChangeEvent e) {
final String propertyName = e.getPropertyName();
final JSpinner spinner = (JSpinner)(e.getSource());
final JSpinner spinner = (JSpinner) (e.getSource());
final SpinnerUI spinnerUI = spinner.getUI();
if (spinnerUI instanceof AquaSpinnerUI) {
final AquaSpinnerUI ui = (AquaSpinnerUI)spinnerUI;
final AquaSpinnerUI ui = (AquaSpinnerUI) spinnerUI;
if ("editor".equals(propertyName)) {
final JComponent oldEditor = (JComponent)e.getOldValue();
final JComponent newEditor = (JComponent)e.getNewValue();
final JComponent oldEditor = (JComponent) e.getOldValue();
final JComponent newEditor = (JComponent) e.getNewValue();
ui.replaceEditor(oldEditor, newEditor);
ui.updateEnabledState();
} else if ("componentOrientation".equals(propertyName)) {
ComponentOrientation o
= (ComponentOrientation) e.getNewValue();
if (o != e.getOldValue()) {
JComponent editor = spinner.getEditor();
if (editor != null) {
editor.applyComponentOrientation(o);
}
spinner.revalidate();
spinner.repaint();
}
} else if ("enabled".equals(propertyName)) {
ui.updateEnabledState();
} else if (JComponent.TOOL_TIP_TEXT_KEY.equals(propertyName)) {
@ -683,8 +735,8 @@ public class AquaSpinnerUI extends SpinnerUI {
} else if ("font".equals(propertyName)) {
JComponent editor = spinner.getEditor();
if (editor instanceof JSpinner.DefaultEditor) {
JTextField tf =
((JSpinner.DefaultEditor) editor).getTextField();
JTextField tf
= ((JSpinner.DefaultEditor) editor).getTextField();
if (tf != null) {
if (tf.getFont() instanceof UIResource) {
tf.setFont(new FontUIResource(spinner.getFont()));
@ -703,12 +755,12 @@ public class AquaSpinnerUI extends SpinnerUI {
final Component[] children = spinnerComponent.getComponents();
for (final Component element : children) {
if (element instanceof JSpinner.DefaultEditor) {
final JTextField tf = ((JSpinner.DefaultEditor)element).getTextField();
final JTextField tf = ((JSpinner.DefaultEditor) element).getTextField();
if (tf != null) {
tf.setToolTipText(toolTipText);
}
} else if (element instanceof JComponent) {
((JComponent)element).setToolTipText(toolTipText);
((JComponent) element).setToolTipText(toolTipText);
}
}
}

View File

@ -32,6 +32,8 @@ import javax.swing.SpinnerDateModel;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
/**
* @test
@ -44,18 +46,28 @@ public class bug8008657 {
private static Robot robot;
private static JSpinner spinner;
private static JFrame frame;
public static void main(String[] args) throws Exception {
robot = new Robot();
UIManager.LookAndFeelInfo[] lookAndFeelArray
= UIManager.getInstalledLookAndFeels();
for (UIManager.LookAndFeelInfo lookAndFeelItem : lookAndFeelArray) {
executeCase(lookAndFeelItem.getClassName());
}
SwingUtilities.invokeAndWait(() -> {
}
static void executeCase(String lookAndFeelString) throws Exception {
if (tryLookAndFeel(lookAndFeelString)) {
SwingUtilities.invokeAndWait(() -> {
createDateSpinner();
createAndShowUI();
});
robot.waitForIdle();
testSpinner(false);
cleanUp();
SwingUtilities.invokeAndWait(() -> {
createNumberSpinner();
@ -64,14 +76,16 @@ public class bug8008657 {
robot.waitForIdle();
testSpinner(true);
cleanUp();
}
}
static void testSpinner(boolean checkHorizontalAligment)
throws Exception {
SwingUtilities.invokeAndWait(() -> {
spinner.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
});
});
robot.waitForIdle();
SwingUtilities.invokeAndWait(() -> {
@ -145,10 +159,34 @@ public class bug8008657 {
}
static void createAndShowUI() {
JFrame frame = new JFrame("Test");
frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 100);
frame.getContentPane().add(spinner);
frame.setVisible(true);
}
private static void cleanUp() throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
frame.dispose();
}
});
}
private static boolean tryLookAndFeel(String lookAndFeelString)
throws Exception {
try {
UIManager.setLookAndFeel(
lookAndFeelString);
} catch (UnsupportedLookAndFeelException
| ClassNotFoundException
| InstantiationException
| IllegalAccessException e) {
return false;
}
return true;
}
}