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