1 /* 2 * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.control; 27 28 import com.sun.javafx.collections.MappingChange; 29 import com.sun.javafx.collections.NonIterableChange; 30 import static javafx.scene.control.SelectionMode.SINGLE; 31 32 import java.util.AbstractList; 33 import java.util.ArrayList; 34 import java.util.BitSet; 35 import java.util.Collections; 36 import java.util.List; 37 38 import javafx.collections.ListChangeListener; 39 import javafx.collections.ObservableList; 40 import javafx.collections.ListChangeListener.Change; 41 import javafx.util.Callback; 42 43 import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList; 44 45 46 /** 47 * An abstract class that implements more of the abstract MultipleSelectionModel 48 * abstract class. However, this class is package-protected and not intended 49 * for public use. 50 * 51 * @param <T> The type of the underlying data model for the UI control. 52 */ 53 abstract class MultipleSelectionModelBase<T> extends MultipleSelectionModel<T> { 54 55 /*********************************************************************** 56 * * 57 * Constructors * 58 * * 59 **********************************************************************/ 60 61 public MultipleSelectionModelBase() { 62 selectedIndexProperty().addListener(valueModel -> { 63 // we used to lazily retrieve the selected item, but now we just 64 // do it when the selection changes. This is hardly likely to be 65 // expensive, and we still lazily handle the multiple selection 66 // cases over in MultipleSelectionModel. 67 setSelectedItem(getModelItem(getSelectedIndex())); 68 }); 69 70 selectedIndices = new BitSet(); 71 72 selectedIndicesSeq = createListFromBitSet(selectedIndices); 73 74 final MappingChange.Map<Integer,T> map = f -> getModelItem(f); 75 76 selectedIndicesSeq.addListener(new ListChangeListener<Integer>() { 77 @Override public void onChanged(final Change<? extends Integer> c) { 78 // when the selectedIndices ObservableList changes, we manually call 79 // the observers of the selectedItems ObservableList. 80 81 // Fix for a bug identified whilst fixing RT-37395: 82 // We shouldn't fire events on the selectedItems list unless 83 // the indices list has actually changed. This means that index 84 // permutation events should not be forwarded blindly through the 85 // items list, as a index permutation implies the items list is 86 // unchanged, not changed! 87 boolean hasRealChangeOccurred = false; 88 while (c.next() && ! hasRealChangeOccurred) { 89 hasRealChangeOccurred = c.wasAdded() || c.wasRemoved(); 90 } 91 92 if (hasRealChangeOccurred) { 93 if (selectedItemChange != null) { 94 selectedItemsSeq.callObservers(selectedItemChange); 95 } else { 96 c.reset(); 97 selectedItemsSeq.callObservers(new MappingChange<Integer, T>(c, map, selectedItemsSeq)); 98 } 99 } 100 c.reset(); 101 } 102 }); 103 104 105 selectedItemsSeq = new ReadOnlyUnbackedObservableList<T>() { 106 @Override public T get(int i) { 107 int pos = selectedIndicesSeq.get(i); 108 return getModelItem(pos); 109 } 110 111 @Override public int size() { 112 return selectedIndices.cardinality(); 113 } 114 }; 115 } 116 117 118 119 /*********************************************************************** 120 * * 121 * Observable properties * 122 * * 123 **********************************************************************/ 124 125 /* 126 * We only maintain the values of the selectedIndex and selectedIndices 127 * properties. The value of the selectedItem and selectedItems properties 128 * is determined on-demand. We fire the SELECTED_ITEM and SELECTED_ITEMS 129 * property change events whenever the related SELECTED_INDEX or 130 * SELECTED_INDICES properties change. 131 * 132 * This means that the cost of the ListViewSelectionModel is cheap in most 133 * cases, assuming that the end-consumer isn't calling getSelectedItems 134 * too aggressively. Of course, this is only an issue when the ListViewModel 135 * is being populated by some remote, expensive to query data source. 136 * 137 * In addition, we do not provide ObservableLists for the selected indices or the 138 * selected items properties, as this would allow the API consumer to add 139 * observers to these ObservableLists. This would make life tougher as we would 140 * then be forced to keep these ObservableLists in-sync at all times, which for 141 * the selectedItems ObservableList, would require potentially a lot of work and 142 * memory. Instead, we return a List, and allow for changes to these Lists 143 * to be observed through the SELECTED_INDICES and SELECTED_ITEMS 144 * properties. 145 */ 146 147 148 final BitSet selectedIndices; 149 final ReadOnlyUnbackedObservableList<Integer> selectedIndicesSeq; 150 @Override public ObservableList<Integer> getSelectedIndices() { 151 return selectedIndicesSeq; 152 } 153 154 private final ReadOnlyUnbackedObservableList<T> selectedItemsSeq; 155 @Override public ObservableList<T> getSelectedItems() { 156 return selectedItemsSeq; 157 } 158 159 160 161 /*********************************************************************** 162 * * 163 * Internal field * 164 * * 165 **********************************************************************/ 166 167 ListChangeListener.Change selectedItemChange; 168 169 // Fix for RT-20945 (and numerous other issues!) 170 private int atomicityCount = 0; 171 boolean isAtomic() { 172 return atomicityCount > 0; 173 } 174 void startAtomic() { 175 atomicityCount++; 176 } 177 void stopAtomic() { 178 atomicityCount = Math.max(0, --atomicityCount); 179 } 180 181 182 /*********************************************************************** 183 * * 184 * Public selection API * 185 * * 186 **********************************************************************/ 187 188 /** 189 * Returns the number of items in the data model that underpins the control. 190 * An example would be that a ListView selection model would likely return 191 * <code>listView.getItems().size()</code>. The valid range of selectable 192 * indices is between 0 and whatever is returned by this method. 193 */ 194 protected abstract int getItemCount(); 195 196 /** 197 * Returns the item at the given index. An example using ListView would be 198 * <code>listView.getItems().get(index)</code>. 199 * 200 * @param index The index of the item that is requested from the underlying 201 * data model. 202 * @return Returns null if the index is out of bounds, or an element of type 203 * T that is related to the given index. 204 */ 205 protected abstract T getModelItem(int index); 206 protected abstract void focus(int index); 207 protected abstract int getFocusedIndex(); 208 209 static class ShiftParams { 210 private final int clearIndex; 211 private final int setIndex; 212 private final boolean selected; 213 214 ShiftParams(int clearIndex, int setIndex, boolean selected) { 215 this.clearIndex = clearIndex; 216 this.setIndex = setIndex; 217 this.selected = selected; 218 } 219 220 public final int getClearIndex() { 221 return clearIndex; 222 } 223 224 public final int getSetIndex() { 225 return setIndex; 226 } 227 228 public final boolean isSelected() { 229 return selected; 230 } 231 } 232 233 // package only 234 void shiftSelection(int position, int shift, final Callback<ShiftParams, Void> callback) { 235 // with no check here, we get RT-15024 236 if (position < 0) return; 237 if (shift == 0) return; 238 239 int selectedIndicesCardinality = selectedIndices.cardinality(); // number of true bits 240 if (selectedIndicesCardinality == 0) return; 241 242 int selectedIndicesSize = selectedIndices.size(); // number of bits reserved 243 244 int[] perm = new int[selectedIndicesSize]; 245 int idx = 0; 246 boolean hasPermutated = false; 247 248 if (shift > 0) { 249 for (int i = selectedIndicesSize - 1; i >= position && i >= 0; i--) { 250 boolean selected = selectedIndices.get(i); 251 252 if (callback == null) { 253 selectedIndices.clear(i); 254 selectedIndices.set(i + shift, selected); 255 } else { 256 callback.call(new ShiftParams(i, i + shift, selected)); 257 } 258 259 if (selected) { 260 perm[idx++] = i + 1; 261 hasPermutated = true; 262 } 263 } 264 selectedIndices.clear(position); 265 } else if (shift < 0) { 266 for (int i = position; i < selectedIndicesSize; i++) { 267 if ((i + shift) < 0) continue; 268 if ((i + 1 + shift) < position) continue; 269 boolean selected = selectedIndices.get(i + 1); 270 271 if (callback == null) { 272 selectedIndices.clear(i + 1); 273 selectedIndices.set(i + 1 + shift, selected); 274 } else { 275 callback.call(new ShiftParams(i + 1, i + 1 + shift, selected)); 276 } 277 278 if (selected) { 279 perm[idx++] = i; 280 hasPermutated = true; 281 } 282 } 283 } 284 285 // This ensure that the selection remains accurate when a shift occurs. 286 final int selectedIndex = getSelectedIndex(); 287 if (selectedIndex >= position && selectedIndex > -1) { 288 // Fix for RT-38787: we used to not enter this block if 289 // selectedIndex + shift resulted in a value less than zero, whereas 290 // now we just set the newSelectionLead to zero in that instance. 291 // There exists unit tests that cover this. 292 final int newSelectionLead = Math.max(0, selectedIndex + shift); 293 294 setSelectedIndex(newSelectionLead); 295 296 // added the selectedIndices call for RT-30356. 297 // changed to check if hasPermutated, and to call select(..) for RT-40010. 298 // This forces the selection event to go through the system and fire 299 // the necessary events. 300 if (hasPermutated) { 301 selectedIndices.set(newSelectionLead, true); 302 } else { 303 select(newSelectionLead); 304 } 305 306 // removed due to RT-27185 307 // focus(newSelectionLead); 308 } 309 310 if (hasPermutated) { 311 selectedIndicesSeq.callObservers( 312 new NonIterableChange.SimplePermutationChange<Integer>( 313 0, 314 selectedIndicesCardinality, 315 perm, 316 selectedIndicesSeq)); 317 } 318 } 319 320 @Override public void clearAndSelect(int row) { 321 if (row < 0 || row >= getItemCount()) { 322 clearSelection(); 323 return; 324 } 325 326 final boolean wasSelected = isSelected(row); 327 328 // RT-33558 if this method has been called with a given row, and that 329 // row is the only selected row currently, then this method becomes a no-op. 330 if (wasSelected && getSelectedIndices().size() == 1) { 331 // before we return, we double-check that the selected item 332 // is equal to the item in the given index 333 if (getSelectedItem() == getModelItem(row)) { 334 return; 335 } 336 } 337 338 // firstly we make a copy of the selection, so that we can send out 339 // the correct details in the selection change event. 340 // We remove the new selection from the list seeing as it is not removed. 341 BitSet selectedIndicesCopy = new BitSet(); 342 selectedIndicesCopy.or(selectedIndices); 343 selectedIndicesCopy.clear(row); 344 List<Integer> previousSelectedIndices = createListFromBitSet(selectedIndicesCopy); 345 346 // RT-32411 We used to call quietClearSelection() here, but this 347 // resulted in the selectedItems and selectedIndices lists never 348 // reporting that they were empty. 349 // makeAtomic toggle added to resolve RT-32618 350 startAtomic(); 351 352 // then clear the current selection 353 clearSelection(); 354 355 // and select the new row 356 select(row); 357 stopAtomic(); 358 359 // fire off a single add/remove/replace notification (rather than 360 // individual remove and add notifications) - see RT-33324 361 ListChangeListener.Change<Integer> change; 362 363 /* 364 * getFrom() documentation: 365 * If wasAdded is true, the interval contains all the values that were added. 366 * If wasPermutated is true, the interval marks the values that were permutated. 367 * If wasRemoved is true and wasAdded is false, getFrom() and getTo() should 368 * return the same number - the place where the removed elements were positioned in the list. 369 */ 370 if (wasSelected) { 371 change = ControlUtils.buildClearAndSelectChange(selectedIndicesSeq, previousSelectedIndices, row); 372 } else { 373 int changeIndex = selectedIndicesSeq.indexOf(row); 374 change = new NonIterableChange.GenericAddRemoveChange<>( 375 changeIndex, changeIndex+1, previousSelectedIndices, selectedIndicesSeq); 376 } 377 378 selectedIndicesSeq.callObservers(change); 379 } 380 381 @Override public void select(int row) { 382 if (row == -1) { 383 clearSelection(); 384 return; 385 } 386 if (row < 0 || row >= getItemCount()) { 387 return; 388 } 389 390 boolean isSameRow = row == getSelectedIndex(); 391 T currentItem = getSelectedItem(); 392 T newItem = getModelItem(row); 393 boolean isSameItem = newItem != null && newItem.equals(currentItem); 394 boolean fireUpdatedItemEvent = isSameRow && ! isSameItem; 395 396 startAtomic(); 397 if (! selectedIndices.get(row)) { 398 if (getSelectionMode() == SINGLE) { 399 quietClearSelection(); 400 } 401 selectedIndices.set(row); 402 } 403 404 setSelectedIndex(row); 405 focus(row); 406 407 stopAtomic(); 408 409 if (! isAtomic()) { 410 int changeIndex = selectedIndicesSeq.indexOf(row); 411 selectedIndicesSeq.callObservers(new NonIterableChange.SimpleAddChange<Integer>(changeIndex, changeIndex+1, selectedIndicesSeq)); 412 } 413 414 if (fireUpdatedItemEvent) { 415 setSelectedItem(newItem); 416 } 417 } 418 419 @Override public void select(T obj) { 420 // if (getItemCount() <= 0) return; 421 422 if (obj == null && getSelectionMode() == SelectionMode.SINGLE) { 423 clearSelection(); 424 return; 425 } 426 427 // We have no option but to iterate through the model and select the 428 // first occurrence of the given object. Once we find the first one, we 429 // don't proceed to select any others. 430 Object rowObj = null; 431 for (int i = 0, max = getItemCount(); i < max; i++) { 432 rowObj = getModelItem(i); 433 if (rowObj == null) continue; 434 435 if (rowObj.equals(obj)) { 436 if (isSelected(i)) { 437 return; 438 } 439 440 if (getSelectionMode() == SINGLE) { 441 quietClearSelection(); 442 } 443 444 select(i); 445 return; 446 } 447 } 448 449 // if we are here, we did not find the item in the entire data model. 450 // Even still, we allow for this item to be set to the give object. 451 // We expect that in concrete subclasses of this class we observe the 452 // data model such that we check to see if the given item exists in it, 453 // whilst SelectedIndex == -1 && SelectedItem != null. 454 setSelectedIndex(-1); 455 setSelectedItem(obj); 456 } 457 458 @Override public void selectIndices(int row, int... rows) { 459 if (rows == null || rows.length == 0) { 460 select(row); 461 return; 462 } 463 464 /* 465 * Performance optimisation - if multiple selection is disabled, only 466 * process the end-most row index. 467 */ 468 469 int rowCount = getItemCount(); 470 471 if (getSelectionMode() == SINGLE) { 472 quietClearSelection(); 473 474 for (int i = rows.length - 1; i >= 0; i--) { 475 int index = rows[i]; 476 if (index >= 0 && index < rowCount) { 477 selectedIndices.set(index); 478 select(index); 479 break; 480 } 481 } 482 483 if (selectedIndices.isEmpty()) { 484 if (row > 0 && row < rowCount) { 485 selectedIndices.set(row); 486 select(row); 487 } 488 } 489 490 selectedIndicesSeq.callObservers(new NonIterableChange.SimpleAddChange<Integer>(0, 1, selectedIndicesSeq)); 491 } else { 492 final List<Integer> actualSelectedRows = new ArrayList<Integer>(); 493 494 int lastIndex = -1; 495 if (row >= 0 && row < rowCount) { 496 lastIndex = row; 497 if (! selectedIndices.get(row)) { 498 selectedIndices.set(row); 499 actualSelectedRows.add(row); 500 } 501 } 502 503 for (int i = 0; i < rows.length; i++) { 504 int index = rows[i]; 505 if (index < 0 || index >= rowCount) continue; 506 lastIndex = index; 507 508 if (! selectedIndices.get(index)) { 509 selectedIndices.set(index); 510 actualSelectedRows.add(index); 511 } 512 } 513 514 if (lastIndex != -1) { 515 setSelectedIndex(lastIndex); 516 focus(lastIndex); 517 setSelectedItem(getModelItem(lastIndex)); 518 } 519 520 // need to come up with ranges based on the actualSelectedRows, and 521 // then fire the appropriate number of changes. We also need to 522 // translate from a desired row to select to where that row is 523 // represented in the selectedIndices list. For example, 524 // we may have requested to select row 5, and the selectedIndices 525 // list may therefore have the following: [1,4,5], meaning row 5 526 // is in position 2 of the selectedIndices list 527 Collections.sort(actualSelectedRows); 528 Change<Integer> change = createRangeChange(selectedIndicesSeq, actualSelectedRows); 529 selectedIndicesSeq.callObservers(change); 530 } 531 } 532 533 static Change<Integer> createRangeChange(final ObservableList<Integer> list, final List<Integer> addedItems) { 534 Change<Integer> change = new Change<Integer>(list) { 535 private final int[] EMPTY_PERM = new int[0]; 536 private final int addedSize = addedItems.size(); 537 538 private boolean invalid = true; 539 540 private int pos = 0; 541 private int from = pos; 542 private int to = pos; 543 544 @Override public int getFrom() { 545 checkState(); 546 return from; 547 } 548 549 @Override public int getTo() { 550 checkState(); 551 return to; 552 } 553 554 @Override public List<Integer> getRemoved() { 555 checkState(); 556 return Collections.<Integer>emptyList(); 557 } 558 559 @Override protected int[] getPermutation() { 560 checkState(); 561 return EMPTY_PERM; 562 } 563 564 @Override public int getAddedSize() { 565 return to - from; 566 } 567 568 @Override public boolean next() { 569 if (pos >= addedSize) return false; 570 571 // starting from pos, we keep going until the value is 572 // not the next value 573 int startValue = addedItems.get(pos++); 574 from = list.indexOf(startValue); 575 to = from + 1; 576 int endValue = startValue; 577 while (pos < addedSize) { 578 int previousEndValue = endValue; 579 endValue = addedItems.get(pos++); 580 ++to; 581 if (previousEndValue != (endValue - 1)) { 582 break; 583 } 584 } 585 586 if (invalid) { 587 invalid = false; 588 return true; 589 } 590 591 // we keep going until we've represented all changes! 592 return pos < addedSize; 593 } 594 595 @Override public void reset() { 596 invalid = true; 597 pos = 0; 598 } 599 600 private void checkState() { 601 if (invalid) { 602 throw new IllegalStateException("Invalid Change state: next() must be called before inspecting the Change."); 603 } 604 } 605 606 }; 607 return change; 608 } 609 610 @Override public void selectAll() { 611 if (getSelectionMode() == SINGLE) return; 612 613 if (getItemCount() <= 0) return; 614 615 final int rowCount = getItemCount(); 616 final int focusedIndex = getFocusedIndex(); 617 618 // set all selected indices to true 619 clearSelection(); 620 selectedIndices.set(0, rowCount, true); 621 selectedIndicesSeq.callObservers(new NonIterableChange.SimpleAddChange<>(0, rowCount, selectedIndicesSeq)); 622 623 if (focusedIndex == -1) { 624 setSelectedIndex(rowCount - 1); 625 focus(rowCount - 1); 626 } else { 627 setSelectedIndex(focusedIndex); 628 focus(focusedIndex); 629 } 630 } 631 632 @Override public void selectFirst() { 633 if (getSelectionMode() == SINGLE) { 634 quietClearSelection(); 635 } 636 637 if (getItemCount() > 0) { 638 select(0); 639 } 640 } 641 642 @Override public void selectLast() { 643 if (getSelectionMode() == SINGLE) { 644 quietClearSelection(); 645 } 646 647 int numItems = getItemCount(); 648 if (numItems > 0 && getSelectedIndex() < numItems - 1) { 649 select(numItems - 1); 650 } 651 } 652 653 @Override public void clearSelection(int index) { 654 if (index < 0) return; 655 656 // TODO shouldn't directly access like this 657 // TODO might need to update focus and / or selected index/item 658 boolean wasEmpty = selectedIndices.isEmpty(); 659 selectedIndices.clear(index); 660 661 if (! wasEmpty && selectedIndices.isEmpty()) { 662 clearSelection(); 663 } 664 665 if (!isAtomic()) { 666 // we pass in (index, index) here to represent that nothing was added 667 // in this change. 668 selectedIndicesSeq.callObservers( 669 new NonIterableChange.GenericAddRemoveChange<>(index, index, 670 Collections.singletonList(index), selectedIndicesSeq)); 671 } 672 } 673 674 @Override public void clearSelection() { 675 List<Integer> removed = new AbstractList<Integer>() { 676 final BitSet clone = (BitSet) selectedIndices.clone(); 677 678 @Override public Integer get(int index) { 679 return clone.nextSetBit(index); 680 } 681 682 @Override public int size() { 683 return clone.cardinality(); 684 } 685 }; 686 687 quietClearSelection(); 688 689 if (! isAtomic()) { 690 setSelectedIndex(-1); 691 focus(-1); 692 selectedIndicesSeq.callObservers( 693 new NonIterableChange.GenericAddRemoveChange<>(0, 0, 694 removed, selectedIndicesSeq)); 695 } 696 } 697 698 private void quietClearSelection() { 699 selectedIndices.clear(); 700 } 701 702 @Override public boolean isSelected(int index) { 703 // Note the change in semantics here - we used to check to ensure that 704 // the index is less than the item count, but now simply ensure that 705 // it is less than the length of the selectedIndices bitset. This helps 706 // to resolve issues such as RT-26721, where isSelected(int) was being 707 // called for indices that exceeded the item count, as a TreeItem (e.g. 708 // the root) was being collapsed. 709 // if (index >= 0 && index < getItemCount()) { 710 if (index >= 0 && index < selectedIndices.length()) { 711 return selectedIndices.get(index); 712 } 713 714 return false; 715 } 716 717 @Override public boolean isEmpty() { 718 return selectedIndices.isEmpty(); 719 } 720 721 @Override public void selectPrevious() { 722 int focusIndex = getFocusedIndex(); 723 724 if (getSelectionMode() == SINGLE) { 725 quietClearSelection(); 726 } 727 728 if (focusIndex == -1) { 729 select(getItemCount() - 1); 730 } else if (focusIndex > 0) { 731 select(focusIndex - 1); 732 } 733 } 734 735 @Override public void selectNext() { 736 int focusIndex = getFocusedIndex(); 737 738 if (getSelectionMode() == SINGLE) { 739 quietClearSelection(); 740 } 741 742 if (focusIndex == -1) { 743 select(0); 744 } else if (focusIndex != getItemCount() -1) { 745 select(focusIndex + 1); 746 } 747 } 748 749 750 751 /*********************************************************************** 752 * * 753 * Private implementation * 754 * * 755 **********************************************************************/ 756 757 private ReadOnlyUnbackedObservableList<Integer> createListFromBitSet(final BitSet bitset) { 758 return new ReadOnlyUnbackedObservableList<Integer>() { 759 private int lastGetIndex = -1; 760 private int lastGetValue = -1; 761 762 @Override public Integer get(int index) { 763 final int itemCount = getItemCount(); 764 if (index < 0 || index >= itemCount) return -1; 765 766 if (index == (lastGetIndex + 1) && lastGetValue < itemCount) { 767 // we're iterating forward in order, short circuit for 768 // performance reasons (RT-39776) 769 lastGetIndex++; 770 lastGetValue = bitset.nextSetBit(lastGetValue + 1); 771 return lastGetValue; 772 } else if (index == (lastGetIndex - 1) && lastGetValue > 0) { 773 // we're iterating backward in order, short circuit for 774 // performance reasons (RT-39776) 775 lastGetIndex--; 776 lastGetValue = bitset.previousSetBit(lastGetValue - 1); 777 return lastGetValue; 778 } else { 779 for (lastGetIndex = 0, lastGetValue = bitset.nextSetBit(0); 780 lastGetValue >= 0 || lastGetIndex == index; 781 lastGetIndex++, lastGetValue = bitset.nextSetBit(lastGetValue + 1)) { 782 if (lastGetIndex == index) { 783 return lastGetValue; 784 } 785 } 786 } 787 788 return -1; 789 } 790 791 @Override public int size() { 792 return bitset.cardinality(); 793 } 794 795 @Override public boolean contains(Object o) { 796 if (o instanceof Number) { 797 Number n = (Number) o; 798 int index = n.intValue(); 799 800 return index >= 0 && index < bitset.length() && 801 bitset.get(index); 802 } 803 804 return false; 805 } 806 }; 807 } 808 }