1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.lang3.util; 18 19 import java.io.Serializable; 20 import java.util.BitSet; 21 import java.util.Objects; 22 import java.util.stream.IntStream; 23 24 /** 25 * A fluent {@link BitSet} with additional operations. 26 * <p> 27 * Originally from Apache Commons VFS with more added to act as a fluent replacement for {@link java.util.BitSet}. 28 * </p> 29 * @since 3.13.0 30 */ 31 public final class FluentBitSet implements Cloneable, Serializable { 32 33 private static final long serialVersionUID = 1L; 34 35 /** 36 * Working BitSet. 37 */ 38 private final BitSet bitSet; 39 40 /** 41 * Creates a new bit set. All bits are initially {@code false}. 42 */ FluentBitSet()43 public FluentBitSet() { 44 this(new BitSet()); 45 } 46 47 /** 48 * Creates a new instance for the given bit set. 49 * 50 * @param set The bit set to wrap. 51 */ FluentBitSet(final BitSet set)52 public FluentBitSet(final BitSet set) { 53 this.bitSet = Objects.requireNonNull(set, "set"); 54 } 55 56 /** 57 * Creates a bit set whose initial size is large enough to explicitly represent bits with indices in the range {@code 0} 58 * through {@code nbits-1}. All bits are initially {@code false}. 59 * 60 * @param nbits the initial size of the bit set. 61 * @throws NegativeArraySizeException if the specified initial size is negative. 62 */ FluentBitSet(final int nbits)63 public FluentBitSet(final int nbits) { 64 this(new BitSet(nbits)); 65 } 66 67 /** 68 * Performs a logical <b>AND</b> of this target bit set with the argument bit set. This bit set is modified so that each 69 * bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the 70 * corresponding bit in the bit set argument also had the value {@code true}. 71 * 72 * @param set a bit set. 73 * @return this. 74 */ and(final BitSet set)75 public FluentBitSet and(final BitSet set) { 76 bitSet.and(set); 77 return this; 78 } 79 80 /** 81 * Performs a logical <b>AND</b> of this target bit set with the argument bit set. This bit set is modified so that each 82 * bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the 83 * corresponding bit in the bit set argument also had the value {@code true}. 84 * 85 * @param set a bit set. 86 * @return this. 87 */ and(final FluentBitSet set)88 public FluentBitSet and(final FluentBitSet set) { 89 bitSet.and(set.bitSet); 90 return this; 91 } 92 93 /** 94 * Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}. 95 * 96 * @param set the {@link BitSet} with which to mask this {@link BitSet}. 97 * @return this. 98 */ andNot(final BitSet set)99 public FluentBitSet andNot(final BitSet set) { 100 bitSet.andNot(set); 101 return this; 102 } 103 104 /** 105 * Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}. 106 * 107 * @param set the {@link BitSet} with which to mask this {@link BitSet}. 108 * @return this. 109 */ andNot(final FluentBitSet set)110 public FluentBitSet andNot(final FluentBitSet set) { 111 this.bitSet.andNot(set.bitSet); 112 return this; 113 } 114 115 /** 116 * Gets the wrapped bit set. 117 * 118 * @return the wrapped bit set. 119 */ bitSet()120 public BitSet bitSet() { 121 return bitSet; 122 } 123 124 /** 125 * Returns the number of bits set to {@code true} in this {@link BitSet}. 126 * 127 * @return the number of bits set to {@code true} in this {@link BitSet}. 128 */ cardinality()129 public int cardinality() { 130 return bitSet.cardinality(); 131 } 132 133 /** 134 * Sets all of the bits in this BitSet to {@code false}. 135 * 136 * @return this. 137 */ clear()138 public FluentBitSet clear() { 139 bitSet.clear(); 140 return this; 141 } 142 143 /** 144 * Sets the bits specified by the indexes to {@code false}. 145 * 146 * @param bitIndexArray the index of the bit to be cleared. 147 * @throws IndexOutOfBoundsException if the specified index is negative. 148 * @return this. 149 */ clear(final int... bitIndexArray)150 public FluentBitSet clear(final int... bitIndexArray) { 151 for (final int e : bitIndexArray) { 152 this.bitSet.clear(e); 153 } 154 return this; 155 } 156 157 /** 158 * Sets the bit specified by the index to {@code false}. 159 * 160 * @param bitIndex the index of the bit to be cleared. 161 * @throws IndexOutOfBoundsException if the specified index is negative. 162 * @return this. 163 */ clear(final int bitIndex)164 public FluentBitSet clear(final int bitIndex) { 165 bitSet.clear(bitIndex); 166 return this; 167 } 168 169 /** 170 * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to 171 * {@code false}. 172 * 173 * @param fromIndex index of the first bit to be cleared. 174 * @param toIndex index after the last bit to be cleared. 175 * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or 176 * {@code fromIndex} is larger than {@code toIndex}. 177 * @return this. 178 */ clear(final int fromIndex, final int toIndex)179 public FluentBitSet clear(final int fromIndex, final int toIndex) { 180 bitSet.clear(fromIndex, toIndex); 181 return this; 182 } 183 184 /** 185 * Cloning this {@link BitSet} produces a new {@link BitSet} that is equal to it. The clone of the bit set is another 186 * bit set that has exactly the same bits set to {@code true} as this bit set. 187 * 188 * @return a clone of this bit set 189 * @see #size() 190 */ 191 @Override clone()192 public Object clone() { 193 return new FluentBitSet((BitSet) bitSet.clone()); 194 } 195 196 @Override equals(final Object obj)197 public boolean equals(final Object obj) { 198 if (this == obj) { 199 return true; 200 } 201 if (!(obj instanceof FluentBitSet)) { 202 return false; 203 } 204 final FluentBitSet other = (FluentBitSet) obj; 205 return Objects.equals(bitSet, other.bitSet); 206 } 207 208 /** 209 * Sets the bit at the specified index to the complement of its current value. 210 * 211 * @param bitIndex the index of the bit to flip. 212 * @throws IndexOutOfBoundsException if the specified index is negative. 213 * @return this. 214 */ flip(final int bitIndex)215 public FluentBitSet flip(final int bitIndex) { 216 bitSet.flip(bitIndex); 217 return this; 218 } 219 220 /** 221 * Sets each bit from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the 222 * complement of its current value. 223 * 224 * @param fromIndex index of the first bit to flip. 225 * @param toIndex index after the last bit to flip. 226 * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or 227 * {@code fromIndex} is larger than {@code toIndex}. 228 * @return this. 229 */ flip(final int fromIndex, final int toIndex)230 public FluentBitSet flip(final int fromIndex, final int toIndex) { 231 bitSet.flip(fromIndex, toIndex); 232 return this; 233 } 234 235 /** 236 * Returns the value of the bit with the specified index. The value is {@code true} if the bit with the index 237 * {@code bitIndex} is currently set in this {@link BitSet}; otherwise, the result is {@code false}. 238 * 239 * @param bitIndex the bit index. 240 * @return the value of the bit with the specified index. 241 * @throws IndexOutOfBoundsException if the specified index is negative. 242 */ get(final int bitIndex)243 public boolean get(final int bitIndex) { 244 return bitSet.get(bitIndex); 245 } 246 247 /** 248 * Returns a new {@link BitSet} composed of bits from this {@link BitSet} from {@code fromIndex} (inclusive) to 249 * {@code toIndex} (exclusive). 250 * 251 * @param fromIndex index of the first bit to include. 252 * @param toIndex index after the last bit to include. 253 * @return a new {@link BitSet} from a range of this {@link BitSet}. 254 * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or 255 * {@code fromIndex} is larger than {@code toIndex}. 256 */ get(final int fromIndex, final int toIndex)257 public FluentBitSet get(final int fromIndex, final int toIndex) { 258 return new FluentBitSet(bitSet.get(fromIndex, toIndex)); 259 } 260 261 @Override hashCode()262 public int hashCode() { 263 return bitSet.hashCode(); 264 } 265 266 /** 267 * Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in 268 * this {@link BitSet}. 269 * 270 * @param set {@link BitSet} to intersect with. 271 * @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}. 272 */ intersects(final BitSet set)273 public boolean intersects(final BitSet set) { 274 return bitSet.intersects(set); 275 } 276 277 /** 278 * Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in 279 * this {@link BitSet}. 280 * 281 * @param set {@link BitSet} to intersect with. 282 * @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}. 283 */ intersects(final FluentBitSet set)284 public boolean intersects(final FluentBitSet set) { 285 return bitSet.intersects(set.bitSet); 286 } 287 288 /** 289 * Returns true if this {@link BitSet} contains no bits that are set to {@code true}. 290 * 291 * @return boolean indicating whether this {@link BitSet} is empty. 292 */ isEmpty()293 public boolean isEmpty() { 294 return bitSet.isEmpty(); 295 } 296 297 /** 298 * Returns the "logical size" of this {@link BitSet}: the index of the highest set bit in the {@link BitSet} plus one. 299 * Returns zero if the {@link BitSet} contains no set bits. 300 * 301 * @return the logical size of this {@link BitSet}. 302 */ length()303 public int length() { 304 return bitSet.length(); 305 } 306 307 /** 308 * Returns the index of the first bit that is set to {@code false} that occurs on or after the specified starting index. 309 * 310 * @param fromIndex the index to start checking from (inclusive). 311 * @return the index of the next clear bit. 312 * @throws IndexOutOfBoundsException if the specified index is negative. 313 */ nextClearBit(final int fromIndex)314 public int nextClearBit(final int fromIndex) { 315 return bitSet.nextClearBit(fromIndex); 316 } 317 318 /** 319 * Returns the index of the first bit that is set to {@code true} that occurs on or after the specified starting index. 320 * If no such bit exists then {@code -1} is returned. 321 * <p> 322 * To iterate over the {@code true} bits in a {@link BitSet}, use the following loop: 323 * </p> 324 * 325 * <pre> 326 * {@code 327 * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { 328 * // operate on index i here 329 * if (i == Integer.MAX_VALUE) { 330 * break; // or (i+1) would overflow 331 * } 332 * }} 333 * </pre> 334 * 335 * @param fromIndex the index to start checking from (inclusive). 336 * @return the index of the next set bit, or {@code -1} if there is no such bit. 337 * @throws IndexOutOfBoundsException if the specified index is negative. 338 */ nextSetBit(final int fromIndex)339 public int nextSetBit(final int fromIndex) { 340 return bitSet.nextSetBit(fromIndex); 341 } 342 343 /** 344 * Performs a logical <b>OR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it 345 * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in 346 * the bit set argument has the value {@code true}. 347 * 348 * @param set a bit set. 349 * @return this. 350 */ or(final BitSet set)351 public FluentBitSet or(final BitSet set) { 352 bitSet.or(set); 353 return this; 354 } 355 356 /** 357 * Performs a logical <b>OR</b> of this bit set with the bit set arguments. This bit set is modified so that a bit in it 358 * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in 359 * the bit set argument has the value {@code true}. 360 * 361 * @param set a bit set. 362 * @return this. 363 */ or(final FluentBitSet... set)364 public FluentBitSet or(final FluentBitSet... set) { 365 for (final FluentBitSet e : set) { 366 this.bitSet.or(e.bitSet); 367 } 368 return this; 369 } 370 371 /** 372 * Performs a logical <b>OR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it 373 * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in 374 * the bit set argument has the value {@code true}. 375 * 376 * @param set a bit set. 377 * @return this. 378 */ or(final FluentBitSet set)379 public FluentBitSet or(final FluentBitSet set) { 380 this.bitSet.or(set.bitSet); 381 return this; 382 } 383 384 /** 385 * Returns the index of the nearest bit that is set to {@code false} that occurs on or before the specified starting 386 * index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned. 387 * 388 * @param fromIndex the index to start checking from (inclusive). 389 * @return the index of the previous clear bit, or {@code -1} if there is no such bit. 390 * @throws IndexOutOfBoundsException if the specified index is less than {@code -1}. 391 */ previousClearBit(final int fromIndex)392 public int previousClearBit(final int fromIndex) { 393 return bitSet.previousClearBit(fromIndex); 394 } 395 396 /** 397 * Returns the index of the nearest bit that is set to {@code true} that occurs on or before the specified starting 398 * index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned. 399 * 400 * <p> 401 * To iterate over the {@code true} bits in a {@link BitSet}, use the following loop: 402 * 403 * <pre> 404 * {@code 405 * for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) { 406 * // operate on index i here 407 * }} 408 * </pre> 409 * 410 * @param fromIndex the index to start checking from (inclusive) 411 * @return the index of the previous set bit, or {@code -1} if there is no such bit 412 * @throws IndexOutOfBoundsException if the specified index is less than {@code -1} 413 */ previousSetBit(final int fromIndex)414 public int previousSetBit(final int fromIndex) { 415 return bitSet.previousSetBit(fromIndex); 416 } 417 418 /** 419 * Sets the bit at the specified indexes to {@code true}. 420 * 421 * @param bitIndexArray a bit index array. 422 * @throws IndexOutOfBoundsException if the specified index is negative. 423 * @return this. 424 */ set(final int... bitIndexArray)425 public FluentBitSet set(final int... bitIndexArray) { 426 for (final int e : bitIndexArray) { 427 bitSet.set(e); 428 } 429 return this; 430 } 431 432 /** 433 * Sets the bit at the specified index to {@code true}. 434 * 435 * @param bitIndex a bit index 436 * @throws IndexOutOfBoundsException if the specified index is negative 437 * @return this. 438 */ set(final int bitIndex)439 public FluentBitSet set(final int bitIndex) { 440 bitSet.set(bitIndex); 441 return this; 442 } 443 444 /** 445 * Sets the bit at the specified index to the specified value. 446 * 447 * @param bitIndex a bit index. 448 * @param value a boolean value to set. 449 * @throws IndexOutOfBoundsException if the specified index is negative. 450 * @return this. 451 */ set(final int bitIndex, final boolean value)452 public FluentBitSet set(final int bitIndex, final boolean value) { 453 bitSet.set(bitIndex, value); 454 return this; 455 } 456 457 /** 458 * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to 459 * {@code true}. 460 * 461 * @param fromIndex index of the first bit to be set. 462 * @param toIndex index after the last bit to be set. 463 * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or 464 * {@code fromIndex} is larger than {@code toIndex}. 465 * @return this. 466 */ set(final int fromIndex, final int toIndex)467 public FluentBitSet set(final int fromIndex, final int toIndex) { 468 bitSet.set(fromIndex, toIndex); 469 return this; 470 } 471 472 /** 473 * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the 474 * specified value. 475 * 476 * @param fromIndex index of the first bit to be set. 477 * @param toIndex index after the last bit to be set. 478 * @param value value to set the selected bits to. 479 * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or 480 * {@code fromIndex} is larger than {@code toIndex}. 481 * @return this. 482 */ set(final int fromIndex, final int toIndex, final boolean value)483 public FluentBitSet set(final int fromIndex, final int toIndex, final boolean value) { 484 bitSet.set(fromIndex, toIndex, value); 485 return this; 486 } 487 488 /** 489 * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to 490 * {@code true}. 491 * 492 * @param fromIndex index of the first bit to be set 493 * @param toIndex index of the last bit to be set 494 * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or 495 * {@code fromIndex} is larger than {@code toIndex} 496 * @return this. 497 */ setInclusive(final int fromIndex, final int toIndex)498 public FluentBitSet setInclusive(final int fromIndex, final int toIndex) { 499 bitSet.set(fromIndex, toIndex + 1); 500 return this; 501 } 502 503 /** 504 * Returns the number of bits of space actually in use by this {@link BitSet} to represent bit values. The maximum 505 * element in the set is the size - 1st element. 506 * 507 * @return the number of bits currently in this bit set. 508 */ size()509 public int size() { 510 return bitSet.size(); 511 } 512 513 /** 514 * Returns a stream of indices for which this {@link BitSet} contains a bit in the set state. The indices are returned 515 * in order, from lowest to highest. The size of the stream is the number of bits in the set state, equal to the value 516 * returned by the {@link #cardinality()} method. 517 * 518 * <p> 519 * The bit set must remain constant during the execution of the terminal stream operation. Otherwise, the result of the 520 * terminal stream operation is undefined. 521 * </p> 522 * 523 * @return a stream of integers representing set indices. 524 * @since 1.8 525 */ stream()526 public IntStream stream() { 527 return bitSet.stream(); 528 } 529 530 /** 531 * Returns a new byte array containing all the bits in this bit set. 532 * 533 * <p> 534 * More precisely, if: 535 * </p> 536 * <ol> 537 * <li>{@code byte[] bytes = s.toByteArray();}</li> 538 * <li>then {@code bytes.length == (s.length()+7)/8} and</li> 539 * <li>{@code s.get(n) == ((bytes[n/8] & (1<<(n%8))) != 0)}</li> 540 * <li>for all {@code n < 8 * bytes.length}.</li> 541 * </ol> 542 * 543 * @return a byte array containing a little-endian representation of all the bits in this bit set 544 */ toByteArray()545 public byte[] toByteArray() { 546 return bitSet.toByteArray(); 547 } 548 549 /** 550 * Returns a new byte array containing all the bits in this bit set. 551 * 552 * <p> 553 * More precisely, if: 554 * </p> 555 * <ol> 556 * <li>{@code long[] longs = s.toLongArray();}</li> 557 * <li>then {@code longs.length == (s.length()+63)/64} and</li> 558 * <li>{@code s.get(n) == ((longs[n/64] & (1L<<(n%64))) != 0)}</li> 559 * <li>for all {@code n < 64 * longs.length}.</li> 560 * </ol> 561 * 562 * @return a byte array containing a little-endian representation of all the bits in this bit set 563 */ toLongArray()564 public long[] toLongArray() { 565 return bitSet.toLongArray(); 566 } 567 568 @Override toString()569 public String toString() { 570 return bitSet.toString(); 571 } 572 573 /** 574 * Performs a logical <b>XOR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it 575 * has the value {@code true} if and only if one of the following statements holds: 576 * <ul> 577 * <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value 578 * {@code false}. 579 * <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value 580 * {@code true}. 581 * </ul> 582 * 583 * @param set a bit set 584 * @return this. 585 */ xor(final BitSet set)586 public FluentBitSet xor(final BitSet set) { 587 bitSet.xor(set); 588 return this; 589 } 590 591 /** 592 * Performs a logical <b>XOR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it 593 * has the value {@code true} if and only if one of the following statements holds: 594 * <ul> 595 * <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value 596 * {@code false}. 597 * <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value 598 * {@code true}. 599 * </ul> 600 * 601 * @param set a bit set 602 * @return this. 603 */ xor(final FluentBitSet set)604 public FluentBitSet xor(final FluentBitSet set) { 605 bitSet.xor(set.bitSet); 606 return this; 607 } 608 609 } 610