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 */
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 */
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 */
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 {@code this} instance.
74 */
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 {@code this} instance.
87 */
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 {@code this} instance.
98 */
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 {@code this} instance.
109 */
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 */
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 */
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 {@code this} instance.
137 */
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 {@code this} instance.
149 */
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 {@code this} instance.
163 */
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 {@code this} instance.
178 */
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
192 public Object clone() {
193 return new FluentBitSet((BitSet) bitSet.clone());
194 }
195
196 @Override
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 {@code this} instance.
214 */
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 {@code this} instance.
229 */
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 */
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 */
257 public FluentBitSet get(final int fromIndex, final int toIndex) {
258 return new FluentBitSet(bitSet.get(fromIndex, toIndex));
259 }
260
261 @Override
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 */
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 */
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 */
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 */
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 */
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 */
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 {@code this} instance.
350 */
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 {@code this} instance.
363 */
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 {@code this} instance.
378 */
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 */
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 */
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 {@code this} instance.
424 */
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 {@code this} instance.
438 */
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 {@code this} instance.
451 */
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 {@code this} instance.
466 */
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 {@code this} instance.
482 */
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} (inclusive) 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 {@code this} instance.
497 */
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 */
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 */
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 */
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 */
564 public long[] toLongArray() {
565 return bitSet.toLongArray();
566 }
567
568 @Override
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 {@code this} instance.
585 */
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 {@code this} instance.
603 */
604 public FluentBitSet xor(final FluentBitSet set) {
605 bitSet.xor(set.bitSet);
606 return this;
607 }
608
609 }