RFR: 8256397: MultipleSelectionModel throws IndexOutOfBoundException
Ajit Ghaisas
aghaisas at openjdk.org
Wed Nov 16 11:14:55 UTC 2022
On Sat, 2 Jul 2022 05:23:22 GMT, Michael Strauß <mstrauss at openjdk.org> wrote:
>> good catch :)
>>
>> No review, just a couple of comments:
>>
>> - other subclasses of MultipleSelectionModelBase might have similar issues
>> - the fix gets rid of the error (good!) but might fire incorrect changes (see below)
>> - there are quite a lot of open issues related to change notification, some might profit from a fix of this
>> - tests, tests, tests .. :)
>>
>> selectIndices might involve disjoint intervals in the change (even for addition which is unusual in the "normal" implementations of observableLists)
>>
>> // initial selection, starting from empty
>> selectionModel.selectIndices(0, /*1, IMPORTANT: remove some index */ 2, 3);
>> System.out.println(change);
>> // expected and actual change
>> { [0, 2, 3] added at 0 }
>>
>> // add selection of disjoint indices (assuming a longer items list)
>> selectionModel.selectIndices(1, 5, 7);
>> System.out.println(change);
>> // actual
>> { [1, 2, 3] added at 1 }
>> // expected
>> { [1] added at 1, [5, 7] added at 4 }
>
> I don't understand how the original code, as well as the proposed solution, can work at all. Let's take @kleopatra's example:
>
> selectionModel.selectIndices(0, 2, 3);
> // ---> expected: { [0, 2, 3] added at 0 }
> selectionModel.selectIndices(1, 5, 7);
> // ---> expected: { [1] added at 1, [5, 7] added at 4 }
>
> In order to find the two sub-ranges `[1]` and `[5, 7]`, we need to compare the _newly added_ indices to the _entire_ list of selected indices. Without doing that, we can't just "know" that there are two contiguous sub-ranges in `[1, 5, 7]`.
>
> However, the algorithm that supposedly tries to do that, doesn't even look at the current list of selected indices (except for a single `indexOf` call, which doesn't do the trick):
>
> int pos = 0;
> int start = 0;
> int end = 0;
>
> // starting from pos, we keep going until the value is
> // not the next value
> int startValue = sortedNewIndices.get(pos++);
> start = indexOf(startValue);
> end = start + 1;
> int endValue = startValue;
> while (pos < size) {
> int previousEndValue = endValue;
> endValue = sortedNewIndices.get(pos++);
> ++end;
> if (previousEndValue != (endValue - 1)) {
> _nextAdd(start, end);
> start = end;
> continue;
> }
>
> // special case for when we get to the point where the loop is about to end
> // and we have uncommitted changes to fire.
> if (pos == size) {
> _nextAdd(start, end);
> }
> }
>
> I think this algorithm mistakenly finds contiguous ranges within the list of _newly added_ indices, which is not what we want.
>
> Here is an algorithm that works:
>
> int startIndex = indexOf(sortedNewIndices.get(0));
> int endIndex = startIndex + 1;
>
> for (int i = 1; i < sortedNewIndices.size(); ++i) {
> int currentValue = get(endIndex);
> int currentNewValue = sortedNewIndices.get(i);
> if (currentValue != currentNewValue) {
> _nextAdd(startIndex, endIndex);
> while (get(endIndex) != currentNewValue) ++endIndex;
> startIndex = endIndex++;
> } else {
> ++endIndex;
> }
>
> if (i == sortedNewIndices.size() - 1) {
> _nextAdd(startIndex, endIndex);
> }
> }
>
>
> Unfortunately, even with the working algorithm, the list change notifications are still not correct in all cases due to another (probably unrelated) bug in `SelectedIndicesList.get(int)`: If I remove the optimization that is implemented in that method, and replace it with a simple lookup, the notifications are always correct in my tests:
>
> @Override public Integer get(int index) {
> for (int lastGetIndex = 0, lastGetValue = bitset.nextSetBit(0);
> lastGetValue >= 0 || lastGetIndex == index;
> lastGetIndex++, lastGetValue = bitset.nextSetBit(lastGetValue + 1)) {
> if (lastGetIndex == index) {
> return lastGetValue;
> }
> }
> return -1;
> }
>
>
> Test 1:
>
> selectionModel.selectIndices(0, 2, 3);
> // ---> { [0, 2, 3] added at 0 }
> selectionModel.selectIndices(1, 5, 7);
> // ---> { [1] added at 1, [5, 7] added at 4 }
>
> Test 2:
>
> selectionModel.selectIndices(1, 3, 4);
> // ---> { [1, 3, 4] added at 0 }
> selectionModel.selectIndices(0, 5, 7);
> // ---> { [0] added at 0, [5, 7] added at 4 }
> selectionModel.selectIndices(6, 8, 9);
> // ---> { [6] added at 5, [8, 9] added at 7 }
@mstr2, can you please review as well?
-------------
PR: https://git.openjdk.org/jfx/pull/353
More information about the openjfx-dev
mailing list