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    *      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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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 this.
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} (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      */
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 this.
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 this.
603      */
604     public FluentBitSet xor(final FluentBitSet set) {
605         bitSet.xor(set.bitSet);
606         return this;
607     }
608 
609 }