View Javadoc
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 }