RFR: 8193286: IntegerSpinnerFactory does not wrap value correctly

Kevin Rushforth kcr at openjdk.java.net
Tue Apr 21 23:36:18 UTC 2020

On Mon, 13 Apr 2020 06:59:08 GMT, Ajit Ghaisas <aghaisas at openjdk.org> wrote:

> Issue : https://bugs.openjdk.java.net/browse/JDK-8193286
> Root Cause :
> Incorrect implementation.
> Current implementation of int wrapValue(int,int,int) in Spinner.java works well if min is 0.
> Hence this implementation works with ListSpinnerValueFactory, but fails with IntegerSpinnerValueFactory.
> Fix :
> Added a method for IntegerSpinnerValueFactory with separate implementation.
> Testing :
> Added unit tests to test increment/decrement in multiple steps and with different amountToStepBy values.
> Also, identified a related behavioral issue https://bugs.openjdk.java.net/browse/JDK-8242553, which will be addressed
> separately.

I checked and there is a case that (mostly) works today that will break with your proposed fix. I left a couple inline

I wonder if it is better to wait and fix it completely in

modules/javafx.controls/src/main/java/javafx/scene/control/Spinner.java line 793:

> 792:      */
> 793:     static int wrapValue(int value, int min, int max, boolean wrapUp) {
> 794:         int ret = 0;

This is essentially a throw-away function. While it does handle the specific case in the bug, namely that of
incrementing or decrementing a positive number of steps of an `amountToStepBy` that is also positive as long as
`currentVal + nsteps * amountToStepBy <= `2 * max`.

It does it by taking a boolean parameter and doing a simple subtractions, which is not how it eventually needs to be
fixed. It won't handle the case of wrapping around by more than `max-min+1` nor will it handle the case of wrapping
around with a negative `amountToStepBy`.

Also, I did find one case where it will yield differently incorrect behavior from today. in the case where min = 0, and
you step by an amount that puts the new value at `newValue > 2 * max` the old code would at least compute an
almost-correct value using `newVal % max`. The new code will cause it to be clamped at max.

modules/javafx.controls/src/main/java/javafx/scene/control/SpinnerValueFactory.java line 581:

> 580:             final int newIndex = getValue() - steps * getAmountToStepBy();
> 581:             setValue(newIndex >= min ? newIndex : (isWrapAround() ? Spinner.wrapValue(newIndex, min, max, false) :
> min)); 582:         }

This will eventually need a new wrapValue function that doesn't take a boolean parameter. The existing one is broken,
but passing a boolean isn't the right answer, either.

modules/javafx.controls/src/test/java/test/javafx/scene/control/SpinnerTest.java line 395:

> 394:     // TODO : This should wrap around and select a value between min and max
> 395:     @Test public void intSpinner_testWrapAround_increment_LargeStep() {
> 396:         intValueFactory.setWrapAround(true);

I don't like the testing for known buggy behavior. If a partial fix is still planned, a better thing would be a test
that verifies correct behavior that is `@Ignore`d pending the final fix.


Changes requested by kcr (Lead).

PR: https://git.openjdk.java.net/jfx/pull/174

More information about the openjfx-dev mailing list